因为你想为Eff
创建一个实例,所以这里有一点小问题,因为我们需要在该类型中包含该行,但正如你可能发现的那样,编译器在这种情况下会抱怨实例头。
一种选择是使用newtype
:
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Exception (EXCEPTION)
import Data.Tuple (Tuple)
import Node.FS (FS)
import Node.Path (FilePath)
type FileType = String
class (Monad m) <= MonadFileSystem m where
cd :: FilePath -> m Unit
ls :: m (Array (Tuple FilePath FileType))
cat :: Array FilePath -> m String
newtype FSEff eff a = FSEff (Eff (fs :: FS, err :: EXCEPTION | eff) a)
runFSEff :: forall eff a. FSEff eff a -> Eff (fs :: FS, err :: EXCEPTION | eff) a
runFSEff (FSEff fse) = fse
derive newtype instance functorFSEff :: Functor (FSEff eff)
derive newtype instance applyFSEff :: Apply (FSEff eff)
derive newtype instance applicativeFSEff :: Applicative (FSEff eff)
derive newtype instance bindFSEff :: Bind (FSEff eff)
derive newtype instance monadFSEff :: Monad (FSEff eff)
instance monadFileSystemFSEff :: MonadFileSystem (FSEff eff) where
cd _ = pure unit
ls = pure []
cat _ = pure "meow"
但也有一些挂羊头卖狗肉,可以使用函数依赖,在那里你可以指定一个像等式约束的行,而不是来完成。这将编译,但使用这种技术真正的但我还没有尝试,所以我不能保证它在更大范围内明确工作:
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Exception (EXCEPTION)
import Data.Tuple (Tuple)
import Node.FS (FS)
import Node.Path (FilePath)
type FileType = String
class (Monad m) <= MonadFileSystem m where
cd :: FilePath -> m Unit
ls :: m (Array (Tuple FilePath FileType))
cat :: Array FilePath -> m String
instance monadFileSystemEff :: EffectRowEquals r (fs :: FS, err :: EXCEPTION | eff) => MonadFileSystem (Eff r) where
cd _ = pure unit
ls = pure []
cat _ = pure "meow"
-- This should probably go in `purescript-type-equality`:
class EffectRowEquals (a ∷ # !) (b ∷ # !) | a → b, b → a where
toER ∷ ∀ r. r a → r b
fromER ∷ ∀ r. r b → r a
instance reflER ∷ EffectRowEquals r r where
toER er = er
fromER er = er
非常感谢真正详细的解答(S)。前者当然适用于仅有副作用的情况,但我仍然很难以这样的方式来制定它,使得它具有您在许多情况下需要的当前工作目录的背景。我尝试了类似于FakeFS实例的类似于Zipper示例的黑客攻击,但破坏了所有漂亮的newtype派生。 第二种解决方案非常可爱且富有表现力,但似乎会生成(虚假?)孤立实例错误,除非与AbstractFileSystem同位置? –
确实,你需要定义后一个以及MonadFileSystem来避免孤儿错误(对于它的另一个非孤儿位置将在模块中使用'Eff',显然不是一个选项!),但是在这里给出你的后续问题,听起来你可能需要'Eff'周围的'StateT'来维护cwd等等 - 无论如何,我可能会去'newtype'路由,这将避免孤儿问题太。 –