Haskell 可扩展效果:另一个效果中的效果
Haskell extensible effects: effect in another effect
我正在尝试使用 extensible-skeleton
包。
在另一个效果中叠加一个效果会导致编译错误。
我尝试了一些其他语言扩展和类型注释,但无法消除这些错误。
如何解决这些错误?
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Main where
import Data.Extensible
import Data.Extensible.Effect
import Data.Type.Equality
type In0 effs = Lookup effs "io" IO
type In1 effs = Lookup effs "reader-float" (ReaderEff Double)
run0 :: forall a. Eff '["io" >: IO] a -> IO a
run0 = retractEff
run1 :: forall effs a. Double -> Eff (("reader-float" >: ReaderEff Double) ': effs) a -> Eff effs a
run1 x = peelEff0 pure $ \Refl k -> k x
lift0 :: forall effs a. In0 effs => IO a -> Eff effs a
lift0 = liftEff (Proxy :: Proxy "io")
ask1 :: forall effs. In1 effs => Eff effs Double
ask1 = askEff (Proxy :: Proxy "reader-float")
eff0 :: forall effs. In0 effs => Eff effs ()
eff0 = do
lift0 $ print "eff0"
run1 2.5 eff1
eff1 :: forall effs. (In0 effs, In1 effs) => Eff effs ()
eff1 = do
x <- ask1
lift0 $ print "eff1"
lift0 $ print (floor x)
main :: IO ()
main = do
run0 eff0
编译时错误:
[1 of 2] Compiling Main
app/Main.hs:32:12: error:
• Couldn't match type ‘membership-0:Type.Membership.Internal.Elaborate
"io" (membership-0:Type.Membership.Internal.FindAssoc 1 "io" effs)’
with ‘'membership-0:Type.Membership.Internal.Expecting (n0 ':> IO)’
arising from a use of ‘eff1’
The type variable ‘n0’ is ambiguous
• In the second argument of ‘run1’, namely ‘eff1’
In a stmt of a 'do' block: run1 2.5 eff1
In the expression:
do lift0 $ print "eff0"
run1 2.5 eff1
• Relevant bindings include
eff0 :: Eff effs () (bound at app/Main.hs:30:1)
|
32 | run1 2.5 eff1
| ^^^^
怎么了?
问题似乎是run1
(顺便说一下,它已经可以作为runReaderEff
使用)接受最外层是"reader-float"
的效果,但是效果eff1
你试图 run1
只满足 In1 effs
,这保证 "reader-float"
在某处,但不一定在最外层。
这反映了这样一个事实,即您不能 运行 将任意层掩埋在堆栈中。您需要从外到内 运行 图层。
有效的是为 eff1
构建显式效果堆栈,然后向上转换:
eff0 :: forall effs. In0 effs => Eff effs ()
eff0 = do
lift0 $ print "eff0"
castEff $ run1 2.5 (eff1 :: Eff '["reader-float" :> ReaderEff Double, "io" :> IO] ())
您似乎想在 effs
前加上 "reader-float"
以表示 eff0
,所以写:
{-# LANGUAGE ScopedTypeVariables #-}
eff0 :: forall effs. In0 effs => Eff effs ()
eff0 = do
lift0 $ print "eff0"
castEff $ run1 2.5 (eff1 :: Eff (("reader-float" :> ReaderEff Double) ': effs) ())
但这不起作用。但是,如果您仔细考虑一下,除了特定的 ["reader-float", "io"]
堆栈之外,没有理由在此处 运行 eff1
。
请记住,在 eff0
和 eff1
的类型签名中使用通用 effs
的原因是为了让它们在任何效果堆栈中与它们所需的层一起使用;通用类型签名是为了调用者的利益。在这种情况下,eff0
正在调用 eff1
,它没有理由向 eff1
提供它需要的层以外的任何东西,即它在本地构建的新 "reader-float"
层,以及它投射到自己的通用 effs
环境中的“io”层。 eff0
或 eff1
对 effs
没有其他兴趣,因此无需将 effs
从 eff0
向下传播到 eff1
。
我正在尝试使用 extensible-skeleton
包。
在另一个效果中叠加一个效果会导致编译错误。
我尝试了一些其他语言扩展和类型注释,但无法消除这些错误。
如何解决这些错误?
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Main where
import Data.Extensible
import Data.Extensible.Effect
import Data.Type.Equality
type In0 effs = Lookup effs "io" IO
type In1 effs = Lookup effs "reader-float" (ReaderEff Double)
run0 :: forall a. Eff '["io" >: IO] a -> IO a
run0 = retractEff
run1 :: forall effs a. Double -> Eff (("reader-float" >: ReaderEff Double) ': effs) a -> Eff effs a
run1 x = peelEff0 pure $ \Refl k -> k x
lift0 :: forall effs a. In0 effs => IO a -> Eff effs a
lift0 = liftEff (Proxy :: Proxy "io")
ask1 :: forall effs. In1 effs => Eff effs Double
ask1 = askEff (Proxy :: Proxy "reader-float")
eff0 :: forall effs. In0 effs => Eff effs ()
eff0 = do
lift0 $ print "eff0"
run1 2.5 eff1
eff1 :: forall effs. (In0 effs, In1 effs) => Eff effs ()
eff1 = do
x <- ask1
lift0 $ print "eff1"
lift0 $ print (floor x)
main :: IO ()
main = do
run0 eff0
编译时错误:
[1 of 2] Compiling Main
app/Main.hs:32:12: error:
• Couldn't match type ‘membership-0:Type.Membership.Internal.Elaborate
"io" (membership-0:Type.Membership.Internal.FindAssoc 1 "io" effs)’
with ‘'membership-0:Type.Membership.Internal.Expecting (n0 ':> IO)’
arising from a use of ‘eff1’
The type variable ‘n0’ is ambiguous
• In the second argument of ‘run1’, namely ‘eff1’
In a stmt of a 'do' block: run1 2.5 eff1
In the expression:
do lift0 $ print "eff0"
run1 2.5 eff1
• Relevant bindings include
eff0 :: Eff effs () (bound at app/Main.hs:30:1)
|
32 | run1 2.5 eff1
| ^^^^
怎么了?
问题似乎是run1
(顺便说一下,它已经可以作为runReaderEff
使用)接受最外层是"reader-float"
的效果,但是效果eff1
你试图 run1
只满足 In1 effs
,这保证 "reader-float"
在某处,但不一定在最外层。
这反映了这样一个事实,即您不能 运行 将任意层掩埋在堆栈中。您需要从外到内 运行 图层。
有效的是为 eff1
构建显式效果堆栈,然后向上转换:
eff0 :: forall effs. In0 effs => Eff effs ()
eff0 = do
lift0 $ print "eff0"
castEff $ run1 2.5 (eff1 :: Eff '["reader-float" :> ReaderEff Double, "io" :> IO] ())
您似乎想在 effs
前加上 "reader-float"
以表示 eff0
,所以写:
{-# LANGUAGE ScopedTypeVariables #-}
eff0 :: forall effs. In0 effs => Eff effs ()
eff0 = do
lift0 $ print "eff0"
castEff $ run1 2.5 (eff1 :: Eff (("reader-float" :> ReaderEff Double) ': effs) ())
但这不起作用。但是,如果您仔细考虑一下,除了特定的 ["reader-float", "io"]
堆栈之外,没有理由在此处 运行 eff1
。
请记住,在 eff0
和 eff1
的类型签名中使用通用 effs
的原因是为了让它们在任何效果堆栈中与它们所需的层一起使用;通用类型签名是为了调用者的利益。在这种情况下,eff0
正在调用 eff1
,它没有理由向 eff1
提供它需要的层以外的任何东西,即它在本地构建的新 "reader-float"
层,以及它投射到自己的通用 effs
环境中的“io”层。 eff0
或 eff1
对 effs
没有其他兴趣,因此无需将 effs
从 eff0
向下传播到 eff1
。