如何使用函数类型依赖来导出类型参数的相等性?

时间:2017-07-30 03:35:05

标签: haskell

请考虑以下代码:

{-# 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类型,我将不胜感激任何建议。

2 个答案:

答案 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建议的类型等价约束。