是否可以通过类型族将类型参数与任意类型匹配?
我试图编写一个类型函数
type family Match s t a
,它具有两个结构s
和t
(假设它们具有相同类型的两个不同参数化,例如Maybe Int
和Maybe 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,允许歧义类型在这里有帮助吗?我不知道陷阱,所以我犹豫了。
谢谢!
答案 0 :(得分:3)
您要的是两种不同的东西。你在找
[类型函数],它具有两个结构,
s
和t
(假定两个相同类型的不同参数化,例如Maybe Int
和Maybe 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 ~ c
或b ~ c
也可能与第一个方程式和第二个方程式匹配,因此,当第一个方程式仍然可用时,跳到后备选项是不正确的。您可能会问:“ a
/ b
和c
为什么不不同?”答案是a
,b
和c
只是变量(恰好是类型变量),而只是值的名称(恰好是类型)。这些值作为函数的参数给出。如果我给您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
由第三个方程式表示。无法使该函数在定义时“选择”第三个方程。