【问题标题】:Why doesn't TypeSynonymInstances allow partially applied type synonyms to be used in instance heads?为什么 TypeSynonymInstances 不允许在实例头中使用部分应用的类型同义词?
【发布时间】:2019-07-22 15:03:08
【问题描述】:

我知道TypeSynomymInstances only allows fully applied type synonyms to be used in instance heads,但如果我也可以使用部分应用的类型同义词似乎会很方便。

例如:

class Example e where
  thingy :: a -> b -> e a b

-- legit, but awkward
newtype FuncWrapper e a b = FuncWrapper { ap :: a -> e a b }
instance (Example e) => Example (FuncWrapper e) where
  thingy _ = FuncWrapper . flip thingy
funcWrapperUse :: (Example e) => e Int String
funcWrapperUse = thingy 1 "two" `ap` 3 `ap` 4 `ap` 5

-- not legal, but a little easier to use
type FuncSynonym e a b = a -> e a b
instance (Example e) => Example (FuncSynonym e) where
  thingy _ = flip thingy
funcSynonymUse :: (Example e) => e Int String
funcSynonymUse = thingy 1 "two" 3 4 5

【问题讨论】:

  • 您是否尝试过-XTypeSynonymInstances 来运行您的第二个版本(按照 GHC 的建议)?
  • @Landel:不喜欢FuncSynonym e 没有完全应用的事实。因此我的问题。
  • 这是(相关)票证785,标记为wontfix
  • -XLiberalTypeSynonyms 让您编写和传递类型同义词

标签: haskell


【解决方案1】:

Haskell 中根本不允许使用部分应用的类型同义词。部分应用的同义词实际上是一个函数,其输入是未应用的类型,其输出是一个类型。例如,这里是一个布尔逻辑的编码:

type True x y = x
type False x y = y
type Not b x y = b y x
type And b1 b2 x y = b1 (b2 x y) y
type Or b1 b2 x y = b1 x (b2 x y)

要确定两个部分应用的类型同义词是否相等,类型检查器必须确定函数是否相等。这是一个很难解决的问题,而且通常是不可判定的。

【讨论】:

  • 很公平。在我的示例中,我使用它的只是一个简单的替换,所以如果有一个选项允许这样做会很好,但话又说回来,我不在 GHC 上工作:)。谢谢!
【解决方案2】:

允许部分应用类型同义词的另一个问题是它们会使类型推断和实例选择基本上不可能。例如,假设在某个程序的上下文中,我想在类型Int -> String -> Int -> (Int, String) 上使用thingythingyforall a b e. a -> b -> e a b 类型,所以我们可以将 aIntbString 统一起来,但如果允许 e 是部分应用的类型同义词,我们可以有

e = FuncSynonym (,)

e = FuncSynonym' Int (,) where type FuncSynonym' x f a b = x -> f a b

甚至

e = Const2 (Int -> (Int, String)) where Const2 a x y = a

类型推断的问题会变得比判断函数相等更糟糕;它需要考虑在特定输入上具有指定输出的所有函数,或者类似的更复杂的问题(想象简单地尝试将a bInt 统一起来)。

【讨论】:

    【解决方案3】:

    我们知道Maybe 的种类是*->*

    所以它可能是Functor的一个实例

       instance Functor Maybe where
           fmap :: f -> Maybe a -> Maybe b
           fmap f Nothing = Nothing
           fmap f (Maybe x)  = Maybe (f x)
    

    第一个例子:

       {-# LANGUAGE TypeSynonymInstances #-}
    
       type MaybeAlias a = Maybe
    
       instance {-# OVERLAPPING #-} Functor (MaybeAlias Int) where
           fmap f functor = undefined
    

    TypeSynonymInstances扩展的作用下(几乎就像一个字符串替换),它等于

          instance {-# OVERLAPPING #-} Functor Maybe where
           fmap f functor = undefined
    

    没关系,因为allow fully applied type synonyms to be used in instance heads


    见另一个例子:

       {-# LANGUAGE TypeSynonymInstances #-}
    
       type MaybeAlias a b = Maybe
    

    MaybeAlias Int 现在是什么样的?好心人是*->*->*

    为什么?

    正如上面的@heatsink 评论:

    部分应用的同义词实际上是一个函数,其输入是未应用的类型,其输出是一个类型

    现在解释一下:

    type MaybeAlias a b = Maybe的定义下:

    MaybeAlias 就像一个部分应用的函数:

    (MaybeAlias) :: a -> b -> Maybe
    

    MaybeAlias Int 就像一个部分应用的函数:

    (MaybeAlias Int) :: b -> Maybe
    

    Maybe 的种类是 * -> *b 的种类是 *

    所以MaybeAlias Int 的种类是* -> (* -> *)

    * -> (* -> *) 等于* -> * -> *

    下面代码不起作用的根本原因,因为Functor typeclass 只接受类型为* -> *,而不是* -> * ->*

       {-# LANGUAGE TypeSynonymInstances #-}
    
       type MaybeAlias a b = Maybe
    
       instance {-# OVERLAPPING #-} Functor (MaybeAlias Int) where
           fmap f functor = undefined
    

    为什么下面的代码不起作用?

    class Example e where
      thingy :: a -> b -> e a b
    
    -- legit, but awkward
    newtype FuncWrapper e a b = FuncWrapper { ap :: a -> e a b }
    instance (Example e) => Example (FuncWrapper e) where
      thingy _ = FuncWrapper . flip thingy
    funcWrapperUse :: (Example e) => e Int String
    funcWrapperUse = thingy 1 "two" `ap` 3 `ap` 4 `ap` 5
    
    -- not legal, but a little easier to use
    type FuncSynonym e a b = a -> e a b
    instance (Example e) => Example (FuncSynonym e) where
      thingy _ = flip thingy
    funcSynonymUse :: (Example e) => e Int String
    funcSynonymUse = thingy 1 "two" 3 4 5
    
    

    Example typeclass 接受具有* -> * -> * 类型的类型

    FuncSynonym 就像一个部分应用的函数:

       FuncSynonym :: e -> a -> b -> (a -> e a b)
    

    FuncSynonym e 就像一个部分应用的函数:

       (FuncSynonym e):: a -> b -> ( a -> e a b)
    

    a 的种类是*

    b 的种类是*

    a -> e a b 的那种*

    (FuncSynonym e)的种类是* -> * -> *

    Example typeclass 接受具有 kind * -> * -> * 的类型,但为什么仍然不起作用?

    这是ghc issue 785comment中的另一个原因

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-03
      • 2019-05-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多