2017-02-10 3 views
3

Я работаю над внедрением встроенного DSL в PureScript, используя стиль «наконец без тегов». Репо можно найти по адресу: https://github.com/afcondon/purescript-finally-tagless-exКак реализовать Monadic экземпляр «окончательного бездепозитного» типа в PureScript?

В этом проблема. Учитывая абстрактное определение очень упрощенной файловой системы:

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), который может быть использован в качестве встроенного языка и интерпретированы (или пробега) для оценки в виде строки (альтернативно, вы можете сделать статический анализ структуры вместо того, чтобы превратить его в строку).

В принципе, можно также иметь пример побочного эффекта, который фактически взаимодействует с файловой системой, но который может «интерпретировать» точно такой же встроенный язык. Я бы хотел использовать purescript-node-fs для этого, что означало бы принятие Eff (fs :: FS, err :: EXCEPTION | eff).

Мой вопрос - как реально реализовать «настоящий», эффектный экземпляр? вам нужно изменить подписи cd, ls и cat? или вы можете каким-то образом оценить всю монаду в Eff, чтобы эти функции не переносили Eff в свою подпись?

ответ

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 
+0

Большое спасибо за подробный ответ (ы). Первый, безусловно, работает только для побочных эффектов, я все еще испытываю трудности с формулировкой его таким образом, что он имеет контекст, такой как текущий рабочий каталог, который вам нужен во многих ситуациях. Я попробовал взломать что-то похожее на пример Zipper из экземпляра FakeFS, но это сломает все отличные новые типы. Второе решение действительно прекрасное и выразительное, но, похоже, генерирует (ложную?) Ошибку сиротского экземпляра, за исключением случаев, когда Colocated с AbstractFileSystem? –

+0

Верно, что вам нужно будет определить последнюю, а также «MonadFileSystem», чтобы избежать сиротской ошибки (другое не-сиротное место для нее было бы в модуле с «Эффектом», очевидно, это не вариант!), Но учитывая ваш следующий вопрос здесь, похоже, что вам, вероятно, понадобится «StateT» вокруг «Eff» для поддержки cwd и т. д., и в этом случае я, вероятно, поеду на маршрут «newtype», который позволит избежать проблемы с сиротой слишком. –

Смежные вопросы