如何实现一个"最终无标记"的Monadic实例PureScript中的类型类?

时间:2017-02-10 18:59:45

标签: dsl purescript

我正在使用"最终无标记"在PureScript中实现嵌入式DSL。样式。回购可以在https://github.com/afcondon/purescript-finally-tagless-ex

找到

这就是问题所在。给出一个非常简化的文件系统的抽象定义:

class (Monad m) <= MonadFileSystem m where
    cd  :: FilePath       -> m Unit
    ls  ::                   m (Array (Tuple FilePath FileType))
    cat :: Array FilePath -> m String

可以很容易地提供一个这样的实现(https://github.com/afcondon/purescript-finally-tagless-ex/blob/master/MonadicEx/src/FakeFileSystem.purs),它可以用作嵌入式语言并解释(或运行)以作为字符串进行求值(或者你也可以对结构进行静态分析)把它变成一个字符串)。

原则上,人们还可以有一个副作用的例子,它实际上会与文件系统进行交互但是可以解释&#34;完全相同的嵌入式语言。我想使用purescript-node-fs来表示接受Eff (fs :: FS, err :: EXCEPTION | eff)某处。

我的问题是 - 如何实际实现&#34;真实&#34;,有效的实例?您是否需要更改cdlscat的签名?或者你能以某种方式评估Eff中的整个monad,以便这些函数不需要在签名中携带Eff吗?

1 个答案:

答案 0 :(得分:3)

因为你想为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