请考虑以下代码:
{-# LANGUAGE RankNTypes, MultiParamTypeClasses, FunctionalDependencies #-}
data St t = St
{ use_t :: t
}
class S s t | s -> t
where -- Nothing really
newtype P s = P
{ unP :: forall b t. (S s t) =>
St t
-> (St t -> b) -- pok
-> b
}
f :: (S s t) => t -> P s
f t = P $ \s pok -> pok s { use_t = t }
代码看起来很人为,但我们的想法是,类S
用于表示类型参数t
由类型参数s
决定,所以我不会&#39 ; t必须将t
作为类型参数添加到类型P
。
上面的代码总之给出了以下错误Could not deduce (t1 ~ t) from the context (S s t) or from (S s t1)
。此错误消息表明编译器想要使用这些上下文中的一个或另一个,而我希望它会使用它们并从它们结束t1 ~ t
。
如果没有将t
作为类型参数添加到P
类型,我将不胜感激任何建议。
答案 0 :(得分:3)
你不能按照写的那样去做。见Can I magic up type equality from a functional dependency?。但你可以用不同的类来做到这一点:
class t ~ T s => S s t where
type T s :: *
您需要为每个实例定义T
,但至少这并不难。如果有合适的定义,您可以提供T
的默认定义。
答案 1 :(得分:0)
我最终以不同的方式解决了我的问题,因为我的方法还有另一个问题,可能不以这种方式解决。另一个问题如下:
{-# LANGUAGE RankNTypes, MultiParamTypeClasses, FunctionalDependencies #-}
data St t u = St
{ use_u :: u
, use_t :: t
}
class Monad m => S s m t | s -> t
newtype P s u m a = P
{ unP :: forall b t. {-(Monad m,S s m t) => -- Adding this causes problems with f -}
St t u
-> (a -> St t u -> m b) -- pok
-> m b
}
w :: a -> P s u m a
w x = P $ \s pok -> pok x s
f :: (S s m t) => P s u m ()
f = P $ \s pok -> unP (w ()) s pok
main = putStrLn "Hello world!"
在unP
中添加类型约束对于其他解决方案至关重要,但这给我带来了麻烦。 t
类型参数仅用于St
类型,因此我现在使用GADT解决了问题。因此我的原始问题的解决方案变成了:
{-# LANGUAGE RankNTypes, MultiParamTypeClasses, FunctionalDependencies, GADTs #-}
{-# LANGUAGE RecordWildCards #-}
data St s where
St :: (S s t) =>
{ use_t :: t
} -> St s
class S s t | s -> t
newtype P s = P
{ unP :: forall b.
St s
-> (St s -> b) -- pok
-> b
}
-- Apparently the record update syntax is not fully implemented for GADTs, especially when using polymorphic fields.
-- See https://www.reddit.com/r/haskell/comments/3r30pp/updating_polymorphic_records/?st=j5sno89f&sh=4ad675fb
-- If you write your own update functions, trouble can be alleviated a bit using RecordWildCards .
f :: (S s t) => t -> P s
-- f t = P $ \s pok -> pok s { use_t = t }
f t = P $ \s pok -> pok $ update_use_t t s
where
update_use_t t = \(St {..}) -> St {use_t = t, ..}
main = putStrLn "Hello world!"
我的第二个问题的解决方案因此变成了:
{-# LANGUAGE RankNTypes, MultiParamTypeClasses, FunctionalDependencies, GADTs #-}
data St s u where
St :: (Monad m,S s m t) =>
{ use_u :: u
, use_t :: t
} -> St s u
class Monad m => S s m t | s -> t
newtype P s u m a = P
{ unP :: forall b.
St s u
-> (a -> St s u -> m b) -- pok
-> m b
}
w :: a -> P s u m a
w x = P $ \s pok -> pok x s
f :: (S s m t) => P s u m ()
f = P $ \s pok -> unP (w ()) s pok
main = putStrLn "Hello world!"
此解决方案最终仍可能受益于dfeuer建议的类型等价约束。