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
 

Reply via email to