This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 900e47653 feat(bindings/haskell): enhance original `OpMonad` to
support custom IO monad (#2789)
900e47653 is described below
commit 900e47653f6c7629195fce33bc63829e0429f7ac
Author: silver-ymz <[email protected]>
AuthorDate: Sun Aug 6 19:58:18 2023 +0800
feat(bindings/haskell): enhance original `OpMonad` to support custom IO
monad (#2789)
---
.github/workflows/bindings_haskell.yml | 3 +-
bindings/haskell/cabal.project.local | 18 ------
bindings/haskell/haskell-src/OpenDAL.hs | 89 +++++++++++++++++++++++------
bindings/haskell/haskell-src/OpenDAL/FFI.hs | 1 -
bindings/haskell/opendal-hs.cabal | 36 ++++++------
bindings/haskell/test/BasicTest.hs | 2 -
6 files changed, 93 insertions(+), 56 deletions(-)
diff --git a/.github/workflows/bindings_haskell.yml
b/.github/workflows/bindings_haskell.yml
index 7b8112b9c..9639ce810 100644
--- a/.github/workflows/bindings_haskell.yml
+++ b/.github/workflows/bindings_haskell.yml
@@ -55,5 +55,4 @@ jobs:
working-directory: "bindings/haskell"
run: |
cargo build
- LIBRARY_PATH=../../target/debug cabal build
- LD_LIBRARY_PATH=../../target/debug cabal test
+ LIBRARY_PATH=../../target/debug LD_LIBRARY_PATH=../../target/debug
cabal test
diff --git a/bindings/haskell/cabal.project.local
b/bindings/haskell/cabal.project.local
deleted file mode 100644
index b72cb7b11..000000000
--- a/bindings/haskell/cabal.project.local
+++ /dev/null
@@ -1,18 +0,0 @@
--- Licensed to the Apache Software Foundation (ASF) under one
--- or more contributor license agreements. See the NOTICE file
--- distributed with this work for additional information
--- regarding copyright ownership. The ASF licenses this file
--- to you under the Apache License, Version 2.0 (the
--- "License"); you may not use this file except in compliance
--- with the License. You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing,
--- software distributed under the License is distributed on an
--- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
--- KIND, either express or implied. See the License for the
--- specific language governing permissions and limitations
--- under the License.
-
-tests: True
\ No newline at end of file
diff --git a/bindings/haskell/haskell-src/OpenDAL.hs
b/bindings/haskell/haskell-src/OpenDAL.hs
index 773901f5a..1931e037c 100644
--- a/bindings/haskell/haskell-src/OpenDAL.hs
+++ b/bindings/haskell/haskell-src/OpenDAL.hs
@@ -14,7 +14,6 @@
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
-{-# LANGUAGE FlexibleInstances #-}
-- |
-- Module : OpenDAL
@@ -27,17 +26,28 @@
--
-- This module provides Haskell bindings for OpenDAL.
module OpenDAL
- ( OperatorConfig (..),
+ ( -- * Operator APIs
+
+ -- ** Types
+ OperatorConfig (..),
Operator,
Lister,
OpenDALError (..),
ErrorCode (..),
EntryMode (..),
Metadata (..),
- OpMonad,
+ OperatorT (..),
MonadOperation (..),
+
+ -- ** Functions
runOp,
newOperator,
+
+ -- * Lister APIs
+ nextLister,
+
+ -- * Operator Raw APIs
+ -- $raw-operations
readOpRaw,
writeOpRaw,
isExistOpRaw,
@@ -48,13 +58,13 @@ module OpenDAL
statOpRaw,
listOpRaw,
scanOpRaw,
- nextLister,
)
where
import Colog (LogAction, Message, Msg (Msg), (<&))
-import Control.Monad.Except (ExceptT, runExceptT, throwError)
-import Control.Monad.Reader (ReaderT, ask, liftIO, runReaderT)
+import Control.Monad.Except (ExceptT, MonadError, MonadTrans, runExceptT,
throwError)
+import Control.Monad.Reader (MonadIO, MonadReader, ReaderT, ask, liftIO,
runReaderT)
+import Control.Monad.Trans (MonadTrans (lift))
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.HashMap.Strict (HashMap)
@@ -68,8 +78,28 @@ import Foreign.C.String
import GHC.Stack (emptyCallStack)
import OpenDAL.FFI
--- | `OperatorConfig` is the configuration for an `Operator`. Currently, it
contains the scheme, config and log action.
--- Recommend using `OverloadedStrings` to construct a default config.
+-- | `OperatorConfig` is the configuration for an `Operator`.
+-- We recommend using `OverloadedStrings` to construct a default config.
+--
+-- For example:
+--
+-- default config
+--
+-- @
+-- newOperator "memory"
+-- @
+--
+-- custom services config
+--
+-- @
+-- newOperator "memory" {ocConfig = HashMap.fromList [("root", "/tmp")]}
+-- @
+--
+-- enable logging
+--
+-- @
+-- newOperator "memory" {ocLogAction = Just simpleMessageAction}
+-- @
data OperatorConfig = OperatorConfig
{ -- | The scheme of the operator. For example, "s3" or "gcs".
ocScheme :: String,
@@ -83,7 +113,7 @@ instance IsString OperatorConfig where
fromString s = OperatorConfig s HashMap.empty Nothing
-- | `Operator` is the entry for all public blocking APIs.
--- Create an `Operator` with `newOp`.
+-- Create an `Operator` with `newOperator`.
newtype Operator = Operator (ForeignPtr RawOperator)
-- | `Lister` is designed to list entries at given path in a blocking manner.
@@ -149,8 +179,13 @@ data Metadata = Metadata
}
deriving (Eq, Show)
--- | The monad used for OpenDAL operations.
-type OpMonad = ReaderT Operator (ExceptT OpenDALError IO)
+-- | @newtype@ wrapper 'ReaderT' that keeps 'Operator' in its context.
+newtype OperatorT m a = OperatorT
+ {runOperatorT :: ReaderT Operator (ExceptT OpenDALError m) a}
+ deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader
Operator, MonadError OpenDALError)
+
+instance MonadTrans OperatorT where
+ lift = OperatorT . lift . lift
-- | A type class for monads that can perform OpenDAL operations.
class (Monad m) => MonadOperation m where
@@ -188,7 +223,7 @@ class (Monad m) => MonadOperation m where
-- An error will be returned if given path doesn’t end with /.
scanOp :: String -> m Lister
-instance MonadOperation OpMonad where
+instance (MonadIO m) => MonadOperation (OperatorT m) where
readOp path = do
op <- ask
result <- liftIO $ readOpRaw op path
@@ -288,11 +323,30 @@ parseFFIMetadata (FFIMetadata mode cacheControl
contentDisposition contentLength
-- Exported functions
--- | Runs an OpenDAL operation in the 'OpMonad'.
-runOp :: Operator -> OpMonad a -> IO (Either OpenDALError a)
-runOp operator op = runExceptT $ runReaderT op operator
-
--- | Creates a new OpenDAL operator via `HashMap`.
+-- | Runner for 'OperatorT' monad.
+-- This function will run given 'OperatorT' monad with given 'Operator'.
+--
+-- Let's see an example:
+--
+-- @
+-- operation :: MonadOperation m => m ()
+-- operation = __do__
+-- writeOp op "key1" "value1"
+-- writeOp op "key2" "value2"
+-- value1 <- readOp op "key1"
+-- value2 <- readOp op "key2"
+-- @
+--
+-- You can run this operation with 'runOp' function:
+--
+-- @
+-- runOp operator operation
+-- @
+runOp :: Operator -> OperatorT m a -> m (Either OpenDALError a)
+runOp op = runExceptT . flip runReaderT op . runOperatorT
+{-# INLINE runOp #-}
+
+-- | Creates a new OpenDAL operator via `OperatorConfig`.
newOperator :: OperatorConfig -> IO (Either OpenDALError Operator)
newOperator (OperatorConfig scheme hashMap maybeLogger) = do
let keysAndValues = HashMap.toList hashMap
@@ -322,6 +376,7 @@ newOperator (OperatorConfig scheme hashMap maybeLogger) = do
str <- peekCString cStr
logger <& Msg (toEnum (fromIntegral enumSeverity)) emptyCallStack (pack
str)
+-- $raw-operations
-- Functions for performing raw OpenDAL operations are defined below.
-- These functions are not meant to be used directly in most cases.
-- Instead, use the high-level interface provided by the 'MonadOperation' type
class.
diff --git a/bindings/haskell/haskell-src/OpenDAL/FFI.hs
b/bindings/haskell/haskell-src/OpenDAL/FFI.hs
index 40f993fec..ad28f451c 100644
--- a/bindings/haskell/haskell-src/OpenDAL/FFI.hs
+++ b/bindings/haskell/haskell-src/OpenDAL/FFI.hs
@@ -14,7 +14,6 @@
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
-{-# LANGUAGE ForeignFunctionInterface #-}
module OpenDAL.FFI where
diff --git a/bindings/haskell/opendal-hs.cabal
b/bindings/haskell/opendal-hs.cabal
index 892a2d7d4..5fae8c057 100644
--- a/bindings/haskell/opendal-hs.cabal
+++ b/bindings/haskell/opendal-hs.cabal
@@ -1,3 +1,4 @@
+cabal-version: 2.2
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
@@ -15,7 +16,6 @@
-- specific language governing permissions and limitations
-- under the License.
-cabal-version: 2.0
name: opendal-hs
version: 0.1.0.0
license: Apache-2.0
@@ -29,14 +29,7 @@ source-repository head
type: git
location: https://github.com/apache/incubator-opendal
-library
- exposed-modules:
- OpenDAL
- other-modules:
- OpenDAL.FFI
- hs-source-dirs: haskell-src
- default-language: Haskell2010
- extra-libraries: opendal_hs
+common base
ghc-options: -Wall
build-depends:
base >=4.10 && <4.17,
@@ -46,19 +39,30 @@ library
time >=1.10,
co-log >=0.5,
text >=2
+ default-language: Haskell2010
+ default-extensions:
+ OverloadedStrings,
+ ForeignFunctionInterface,
+ DerivingStrategies,
+ GeneralizedNewtypeDeriving,
+ LambdaCase
+
+library
+ import: base
+ extra-libraries: opendal_hs
+ exposed-modules:
+ OpenDAL
+ other-modules:
+ OpenDAL.FFI
+ hs-source-dirs: haskell-src
test-suite opendal-hs-test
+ import: base
type: exitcode-stdio-1.0
main-is: Spec.hs
other-modules: BasicTest
hs-source-dirs: test
- default-language: Haskell2010
- other-extensions: OverloadedStrings
- ghc-options: -Wall
build-depends:
- base,
opendal-hs,
tasty,
- tasty-hunit,
- co-log,
- text
\ No newline at end of file
+ tasty-hunit
\ No newline at end of file
diff --git a/bindings/haskell/test/BasicTest.hs
b/bindings/haskell/test/BasicTest.hs
index dcf16d6d0..77e0a6586 100644
--- a/bindings/haskell/test/BasicTest.hs
+++ b/bindings/haskell/test/BasicTest.hs
@@ -14,8 +14,6 @@
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedStrings #-}
module BasicTest (basicTests) where