2017-02-10 11 views
3

"마지막으로 태그가없는"스타일을 사용하여 PureScript에 내장 된 DSL을 구현하려고합니다. 레포는 https://github.com/afcondon/purescript-finally-tagless-exPureScript에서 "마지막으로 태그가없는"typeclass의 Monadic 인스턴스를 구현하는 방법은 무엇입니까?

에 있습니다. 다음과 같은 문제가 있습니다. 매우 단순화 된 파일 시스템의 추상적 인 정의를 감안할 때 :

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) (또는 실행)로 구현을 제공 할 수 있습니다 (또는 문자열로 변환하는 대신 구조체에서 정적 분석을 수행 할 수도 있습니다).

원칙적으로 파일 시스템과 실제로 상호 작용하지만 정확히 동일한 임베디드 언어를 "해석 할"수있는 부작용 예가있을 수 있습니다. Eff (fs :: FS, err :: EXCEPTION | eff) 어딘가에 받아들이는 것을 의미하는 purescript-node-fs을 사용하고 싶습니다.

내 질문은 - 어떻게 실제로 "실제"효과적인 인스턴스를 구현합니까? cd, lscat의 서명을 변경해야합니까? 또는 당신은 어떻게해서든지 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

정말 자세한 답변을 많이 고마워. 전작은 부작용 만있는 상황에서도 효과가 있습니다. 여러 상황에서 필요한 현재 작업 디렉토리와 같은 컨텍스트가있는 방식으로 작성하는 데 여전히 어려움이 있습니다. 나는 FakeFS 인스턴스의 Zipper 예제와 비슷한 방식으로 해킹을 시도했지만 좋은 newtype 파생물을 모두 파기합니다. 두 번째 솔루션은 정말 아름답고 표현력이 뛰어나지 만, AbstractFileSystem과 함께 배치하지 않으면 고아 인스턴스 오류가 발생하는 것으로 보입니까? –

+0

'MonadFileSystem'과 함께'MonadFileSystem'과 함께 고아 오류를 피할 필요가 있다는 것은 사실입니다 (다른 고아가 아닌 곳은 'Eff'가있는 모듈에 있지만 분명히 옵션이 아닙니다!). 그러나 당신의 후속 질문을 감안할 때, 어쨌든 cwd 등을 유지하기 위해'Eff' 주위에'StateT'가 필요할 것 같아요 .- 만약'newtype' 라우트를 가야한다면 고아 문제를 피할 수있을 것입니다. 너무. –