Haskell 自由单子和类型约束
我正在寻找处理haskell约束的实用策略或技巧,如下例所示Haskell 自由单子和类型约束,haskell,polymorphism,constraints,typeclass,Haskell,Polymorphism,Constraints,Typeclass,我正在寻找处理haskell约束的实用策略或技巧,如下例所示 我有一个函子Choice,我想把解释器从Choice x函子转换成mx,把解释器从Free-Choice x转换成mx -- Choice : endofunctor data Choice next = Choice next next deriving (Show) instance Functor Choice where fmap f (Choice a b) = Choice (f a) (f b) -- I
我有一个函子
Choice
,我想把解释器从Choice x
函子转换成mx
,把解释器从Free-Choice x
转换成mx
-- Choice : endofunctor
data Choice next = Choice next next deriving (Show)
instance Functor Choice where
fmap f (Choice a b) = Choice (f a) (f b)
-- I have a function from the functor to a monad m
inter1 :: Choice x -> IO x
inter1 (Choice a b) = do
x <- readLn :: IO Bool
return $ if x then a else b
-- universal property gives me a function from the free monad to m
go1 :: Free Choice x -> IO x
go1 = interpMonad inter1
然后它找不到要在interp2中应用的显示约束
我怀疑量词是问题所在,所以我简化为
lifting :: (forall x . x -> b) ->
(forall x. x -> b)
lifting = id
lifting2 :: (forall x . Show x => x -> b) ->
(forall x . Show x => x -> b)
lifting2 = id
somefunction :: Show x => x -> String
somefunction = lifting show -- FAILS
somefunction2 :: Show x => x -> String
somefunction2 = lifting2 show -- OK
这突出了一个问题:无法从上下文(Show x)中使用“Show”来推断(Show x1)
我们有两个不同的类型变量,约束不会从一个流向另一个
我可以编写一些专门的函数来处理以下约束(顺便说一句,不起作用),但我的问题是,处理这个问题的实用策略是什么?(相当于未定义,查看类型,继续…)
编辑 根据提供的答案,这里是对提升功能的修改
lifting :: forall b c. Proxy c
-> (forall x . c x => x -> b)
-> (forall x . c x => x -> b)
lifting _ = id
somefunction3 :: Show x => x -> String
somefunction3 = lifting (Proxy :: Proxy Show) show
我看不到您的intermonad函数,因此我将在这里包括一个可能的定义:
interpMonad :: forall f m x . (Functor f, Monad m)
=> (forall y . f y -> m y) -> Free f x -> m x
interpMonad xx = go . runIdentity . runFreeT where
go (FreeF x) = xx x >>= go . runIdentity . runFreeT
go (Pure x) = return x
为了在内部函数上也有类约束,只需将约束添加到内部函数。您还需要对类型Free
进行正确的约束,并且需要额外的代理来帮助类型检查器进行检查。否则,函数的定义相同:
interpMonadC :: forall f m x c . (Functor f, Monad m, c (Free f x))
=> Proxy c
-> (forall y . c y => f y -> m y)
-> (Free f x -> m x)
interpMonadC _ xx = go . runIdentity . runFreeT where
go (FreeF x) = xx x >>= go . runIdentity . runFreeT
go (Pure x) = return x
现在很简单:
>:t interpMonadC (Proxy :: Proxy Show) interp2
interpMonadC (Proxy :: Proxy Show) interp2
:: Show x => Free Choice x -> IO x
因此,通过在调用站点使用代理,我们传递了一个值,我们选择该值的类型为proxy Show
,因此c
类型变量为Show
,该类型用作约束(通过ConstraintTypes选项)。在结果函数和内部函数中使用了相同的约束,这导致编译器在interp2上查找show实例,它很容易在上下文中找到该实例。。。。我能理解这些东西,但我想知道我写它们有多容易是不是interpMonad
aretract
?@arrowd:我忘了retract是什么。但是interMonad采用了一种自然转换interp2(为所有类型x统一定义,显然从选择a
到选择b
然后传输到IO b
的功能与从选择a
到IO a
然后传输到IO b
的功能相同)到另一个自然转变。interp2是内函子范畴中的一个箭头,从作为函子的Choice
到m
,自由/健忘附加项最初为您提供了一个唯一的箭头Intermonad,从作为函子的monadfree Choice
到m
monad@arrowd这是同构Endo(F,U(m))/Endo的一个方向(自由F,m)。对于函子类别,内部更正:“一个唯一的箭头interpMonad'=>”一个唯一的箭头interpMonad interp2
”above@nicolas我一直将约束视为子集。即forall(x:x)。cx=>Proxy x
表示满足谓词C
的x
的子集。
interpMonad :: forall f m x . (Functor f, Monad m)
=> (forall y . f y -> m y) -> Free f x -> m x
interpMonad xx = go . runIdentity . runFreeT where
go (FreeF x) = xx x >>= go . runIdentity . runFreeT
go (Pure x) = return x
interpMonadC :: forall f m x c . (Functor f, Monad m, c (Free f x))
=> Proxy c
-> (forall y . c y => f y -> m y)
-> (Free f x -> m x)
interpMonadC _ xx = go . runIdentity . runFreeT where
go (FreeF x) = xx x >>= go . runIdentity . runFreeT
go (Pure x) = return x
>:t interpMonadC (Proxy :: Proxy Show) interp2
interpMonadC (Proxy :: Proxy Show) interp2
:: Show x => Free Choice x -> IO x