查找匹配的参数参数

时间:2019-03-27 19:22:31

标签: haskell type-families

是否可以通过类型族将类型参数与任意类型匹配?

我试图编写一个类型函数

type family Match s t a

,它具有两个结构st(假设它们具有相同类型的两个不同参数化,例如Maybe IntMaybe String),而参数将被匹配。如果找到匹配的参数,类型函数将提供替换项。否则,它将提供原始参数。

Match (Maybe a) (Maybe b) a ~ b
Match (Maybe a) (Maybe b) c ~ c 

我尝试的实现:

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE PolyKinds #-}

import Data.Type.Equality
import Data.Type.Bool

type family Match (s::k) (t::k) (a :: * ) :: * where
  Match (f a :: *) (g b :: *) c = If ((a == c)) b c

找到参数后,该系列显然按计划工作:

>  :t (undefined :: (Match (Maybe a) (Maybe b) a))
(undefined :: (Match (Maybe a) (Maybe b) a)) :: b

但不适用于不匹配的情况:

 >  :t (undefined :: (Match (Maybe a) (Maybe b) c))
     Couldn't match type ‘If
                             ghc-prim-0.5.0.0:GHC.Types.*
                             (Data.Type.Equality.EqStar a0 c0)
                             b0
                             c0’
                     with ‘If
                             ghc-prim-0.5.0.0:GHC.Types.*   (Data.Type.Equality.EqStar a c) b c’
      Expected type: Match * (Maybe a) (Maybe b) c
        Actual type: Match * (Maybe a0) (Maybe b0) c0
      NB: ‘If’ is a type function, and may not be injective
      The type variables ‘a0’, ‘b0’, ‘c0’ are ambiguous
    • In the ambiguity check for an expression type signature
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      In an expression type signature: Match (Maybe a) (Maybe b) c
      In the expression: (undefined :: Match (Maybe a) (Maybe b) c)

就像我要问的一样吗?不必一定是类型家族,我愿意接受其他方法。 OTOH,允许歧义类型在这里有帮助吗?我不知道陷阱,所以我犹豫了。

谢谢!

1 个答案:

答案 0 :(得分:3)

您要的是两种不同的东西。你在找

  

[类型函数],它具有两个结构,st(假定两个相同类型的不同参数化,例如Maybe IntMaybe String )和要匹配的参数。如果找到匹配的参数,类型函数将提供替换项。否则,它将提供原始参数。

您已经基本上写了它。您可能应该避免使用(==),因为可以使用非线性匹配,这通常是更好的选择:

type family Match (a :: i) (b :: i) (c :: o) :: o where
    Match (f a) (f b) a = b
    Match (f a) (f b) b = a
    Match _     _     c = c

稍后,您定义两个函数,并且都希望它们都进行编译。

undefined :: Match (Maybe a) (Maybe b) a
undefined :: Match (Maybe a) (Maybe b) c

第一个带有两种类型的参数:

\@(a :: Type) @(b :: Type) -> (undefined :: Match (Maybe a) (Maybe b) a)

类型签名与Match的第一个等式匹配,并简化为a

\@(a :: Type) @(b :: Type) -> (undefined :: a)

哪个可以推断出函数undefined的正确参数:

\@a @(b :: Type) -> undefined @'GHC.Types.LiftedRep @a

第二个功能是

\@(a :: Type) @(b :: Type) @(c :: Type) -> (undefined :: Match (Maybe a) (Maybe b) c)

undefined的参数推断为类型族应用程序Match (Maybe a) (Maybe b) c是不正确的。关于“歧义类型”错误,存在许多包含其原因的问题。相反,必须减少类型族应用程序。但是它不能减少。它与第三个方程式匹配,但是如果a ~ cb ~ c也可能与第一个方程式和第二个方程式匹配,因此,当第一个方程式仍然可用时,跳到后备选项是不正确的。您可能会问:“ a / bc为什么不不同?”答案是abc只是变量(恰好是类型变量),而只是值的名称(恰好是类型)。这些值作为函数的参数给出。如果我给您x, y :: Int,并要求您提供if x == y then "Yes" else "No"的值,您将无法告诉我,因为您不知道x和{{1}的值}。但是,您已经问过Haskell,“ y在这里。a, b, c :: Type是什么”这同样无法回答。没办法分辨出相等值的变量,因为这确实会破坏Haskell的基础。

请注意,对于我给您的问题,我的回答很不客气。您可以说“ if a == c then b else if b == c then a else c?的值为if x == y then "Yes" else "No"”。类似地,我可以通过允许模棱两可的类型,手动向if x == y then "Yes" else "No"提供未归约类型族应用程序的类型实参来定义函数。

不再需要通过尝试(并且失败)减少undefined应用程序来推断undefined的论点是什么。

Match

这巧妙地避开了不可能的问题。现在将fun :: forall a b c. Match (Maybe a) (Maybe b) c fun = undefined @_ @(Match (Maybe a) (Maybe b) c) 的结果类型推迟到使用时,即fun的功效/诅咒。现在,AllowAmbiguousTypes由第一个方程式表示,fun @Int @String @Int :: String由第二个方程式表示,fun @Int @String @String :: Int由第三个方程式表示。无法使该函数在定义时“选择”第三个方程。