在构造函数之间编码选择GHC.Generics定义以下类型:
data (:+:) f g p = L1 (f p) | R1 (g p)
Generic类提供了将Generic类型转换为表示的方法:
from :: a -> Rep a x
要编写一个类型泛型的函数,我定义了一个处理表示的类:
class MyClass r where myFun :: r a -> Maybe Int
假设我还有一个我为其定义实例的类SomeClass:
instance (SomeClass (f p),SomeClass (g p)) => SomeClass ((:+:) f g p) where
someFun (R1 _) = Just 42
如何将SomeClass约束添加到Generic sum类型的MyClass实例?换句话说,以下实例有什么问题:
instance (SomeClass (f p), SomeClass (g p), MyClass f, MyClass g)
=> MyClass ((:+:) f g) where
myFun (L1 x) = myFun x
myFun y = someFun y -- Error: Could not deduce (SomeClass (f a))
-- arising from a use of ‘someFun’
我写的一个完整的例子是:
{-# LANGUAGE TypeOperators, DefaultSignatures, DeriveGeneric, FlexibleContexts,
UndecidableInstances, AllowAmbiguousTypes, RankNTypes #-}
module M where
import GHC.Generics
---
class SomeClass a where
someFun :: a -> Maybe Int
default someFun :: (Generic a, MyClass (Rep a)) => a -> Maybe Int
someFun x = myFun (from x)
instance (SomeClass (f p),SomeClass (g p)) => SomeClass ((:+:) f g p) where
someFun (R1 _) = Just 42
instance SomeClass Int where
someFun i = Just i
---
class MyClass r where
myFun :: r a -> Maybe Int
instance (SomeClass a) => MyClass (K1 i a) where
myFun (K1 x) = someFun x -- This is fine
instance (SomeClass (f p), SomeClass (g p), MyClass f, MyClass g) => MyClass ((:+:) f g) where
myFun (L1 x) = myFun x
myFun y = someFun y -- Error: Could not deduce (SomeClass (f a)) arising from a use of ‘someFun’
答案 0 :(得分:3)
如果您向SomeClass a
添加myFun
约束,则无其他事情可做。
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Applicative
import GHC.Generics
class SomeClass a where
someFun :: a -> Maybe Int
class MyClass f where
myFun :: SomeClass a => f a -> Maybe Int
default myFun :: (Generic1 f, MyClass (Rep1 f), SomeClass a) => f a -> Maybe Int
myFun f = myFun (from1 f)
您可以为通用表示中使用的所有数据类型编写实例。其中最有趣的是Par1
,它实际上使用SomeClass a
约束在参数出现时使用someFun
。
-- occurences of the parameter
instance MyClass Par1 where
myFun (Par1 p) = someFun p
-- recursions of kind *
instance SomeClass a => MyClass (K1 i a) where
myFun (K1 a) = someFun a
-- recursions of kind * -> *
instance MyClass f => MyClass (Rec1 f) where
myFun (Rec1 f) = myFun f
-- constructors with no arguments
instance MyClass U1 where
myFun (U1) = Nothing -- or Just 0 or Just 1 depending on what you're doing
-- constructors with multiple arguments
instance (MyClass f, MyClass g) => MyClass (f :*: g) where
myFun (f :*: g) = liftA2 (+) (myFun f) (myFun g) -- or howerever you are going to combine the Maybe Int
-- data types with multiple constructors
instance (MyClass f, MyClass g) => MyClass (f :+: g) where
myFun (L1 f) = myFun f
myFun (R1 g) = myFun g
-- metadata
instance (MyClass f) => MyClass (M1 i c f) where
myFun (M1 f) = myFun f
如果你想支持仿函数的组合,我们必须更聪明一些。明显的定义需要SomeClass (Maybe Int)
实例。
-- composition of functors
instance (MyClass f, MyClass g, Functor f) => MyClass (f :.: g) where
myFun (Comp1 fg) = myFun $ fmap myFun fg
我们会让SomeClass
个实例一般重用MyClass
来获取它们。由于MyClass
的{{1}}需要myFun
个实例,我们需要证明参数SomeClass
永远不会出现在Par1
中。 Rep
将证明该参数为空。
from'
void中的class SomeClass a where
someFun :: a -> Maybe Int
default someFun :: (Generic a, MyClass (Rep a)) => a -> Maybe Int
someFun a = myFun (from' a)
类型表示无法逻辑存在的类型。以下证明Void
的参数始终为空
Generic
为了满足-- Prove that the parameter is always empty
from' :: Generic a => a -> Rep a Void
from' = from
的{{1}}约束,我们为SomeClass
实例配备myFun
个实例。我们可以肯定Void
永远不会被调用,因为没有类型SomeClass
的值传递给它。
someFun :: Void -> Maybe Int
现在我们可以为Void
派生一个实例,假设我们有instance SomeClass Void where
someFun = absurd
个实例。
SomeClass (Maybe Int)
您无需重复使用SomeClass Int
-- The following instances are needed for the composition of functors
instance SomeClass Int where
someFun = Just
instance SomeClass a => SomeClass (Maybe a)
来派生MyClass
个实例。相反,无论参数是什么,您都可以为具有Void
的事物定义另一个类。
SomeClass
您为myFun
和class GSomeClass f where
gsomeFun :: f a -> Maybe Int
以外的所有内容编写GSomeClass
个实例,并使用Par1
派生Rec1
个实例。 GSomeClass
个实例从不使用参数,即使对于SomeClass
等类型也不行;相反,参数Generic
的每次出现都显示为Maybe a
。
a
答案 1 :(得分:0)
Haskell目前无法编写类型forall p. SomeClass (f p) =>
的约束,这是您在类型中尝试做的事情
(SomeClass (f p), SomeClass (g p), MyClass f, MyClass g) => MyClass ((:+:) f g)
使用第二个类型类捕获这些约束是一个技巧。您可以编写代表FSomeClass f
的第二个班级forall p. SomeClass (f p)
。如果SomeClass
很简单,您只需在SomeClass
的字典中为FSomeClass
重新生成字典的字段,并使用f a
代替a
。
class FSomeClass f where
fsomeFun :: f a -> Maybe Int
如果SomeClass
更复杂,或者您需要为依赖于它的代码提供真实的SomeClass
实例,则可以在{{{{{}}中捕获整个SomeClass
字典。 1}}。 GADT
捕获任何约束的字典。
Dict
使用{-# LANGUAGE GADTs #-}
{-# LANGUAGE ConstraintKinds #-}
data Dict c where
Dict :: c => Dict c
我们可以编写一个类,对于每个Dict
,a
都有一个f a
实例被困在SomeClass
。< / p>
Dict
要进入class FSomeClass f where
someDict :: p0 a -> p1 f -> Dict (SomeClass (f a))
-- | | ^ there's a SomeClass (f a) instance
-- | ^ for this f
-- ^ for any a
实例,您需要在字典上进行模式匹配。
我们将为来自SomeClass (f a)
和SomeClass (f p)
个实例的所有类型制作GHC.Generics
个实例,以证明FSomeClass f
可能因所有类型而异。
p
对于{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
,我们无法对参数Par1
执行任何操作,因为它可能会因所有类型而异。我们唯一的另一种选择是不提供p
实例。对于SomeClass (Par1 p)
,Par1
和K1
,U1
个实例没有约束,SomeClass
实例只捕获FSomeClass
字典。
SomeClass
instance SomeClass (Par1 p) where
someFun (Par1 p) = Nothing -- You can't do anything with the p; you know nothing about it.
instance FSomeClass Par1 where
someDict _ _ = Dict
instance SomeClass a => SomeClass (K1 i a p) where
someFun (K1 a) = someFun a
instance SomeClass a => FSomeClass (K1 i a) where
someDict _ _ = Dict
instance SomeClass (U1 p) where
someFun (U1) = Nothing -- or Just 0 or Just 1 depending on what you're doing
instance FSomeClass U1 where
someDict _ _ = Dict
和SomeClass
的{{1}}个实例需要M1
约束。在我们构建Rec1
之前,例如SomeClass (f p) =>
我们必须为Dict
引入字典。我们从SomeClass (Rec1 f p)
SomeClass (f p)
实例获取SomeClass (f p)
字典,并在其FSomeClass
上获取模式匹配。
f
对于产品和总和,Dict
实例有两个约束,因此我们得到两个字典的模式匹配。我们以同样的方式处理仿函数的组合,但我为了简洁而跳过它。
instance SomeClass (f p) => SomeClass (Rec1 f p) where
someFun (Rec1 f) = someFun f
instance (FSomeClass f) => FSomeClass (Rec1 f) where
someDict pa _ = case someDict pa (Proxy :: Proxy f) of Dict -> Dict
instance SomeClass (f p) => SomeClass (M1 i c f p) where
someFun (M1 f) = someFun f
instance (FSomeClass f) => FSomeClass (M1 i c f) where
someDict pa _ = case someDict pa (Proxy :: Proxy f) of Dict -> Dict
使用原始SomeClass
instance (SomeClass (f p), SomeClass (g p)) => SomeClass ((f :*: g) p) where
someFun (f :*: g) = liftA2 (+) (someFun f) (someFun g) -- or howerever you are going to combine the Maybe Int
instance (FSomeClass f, FSomeClass g) => FSomeClass (f :*: g) where
someDict pa _ = case someDict pa (Proxy :: Proxy f) of
Dict -> case someDict pa (Proxy :: Proxy g) of
Dict -> Dict
instance (SomeClass (f p), SomeClass (g p)) => SomeClass ((f :+: g) p) where
someFun (L1 f) = someFun f
someFun (R1 g) = someFun g
instance (FSomeClass f, FSomeClass g) => FSomeClass (f :+: g) where
someDict pa _ = case someDict pa (Proxy :: Proxy f) of
Dict -> case someDict pa (Proxy :: Proxy g) of
Dict -> Dict
我们将为MyClass
编写一个实例。我们将采用您的原始签名,并将class MyClass f where
myFun :: f a -> Maybe Int
替换为:+:
。
SomeClass (f p)
FSomeClass f
的分支字面上使用instance (SomeClass (f p), SomeClass (g p), MyClass f, MyClass g) => MyClass ((:+:) f g)
instance (FSomeClass f, FSomeClass g , MyClass f, MyClass g) => MyClass ((:+:) f g)
。
R1
我不建议为someFun
编写非关联的实例。如果要以不同方式处理第一个构造函数,则应保证以后的构造函数不会以与第一个构造函数相同的方式处理。数据类型
instance (FSomeClass f, FSomeClass g, MyClass f, MyClass g) => MyClass (f :+: g) where
myFun (L1 f) = myFun f
myFun (R1 g) = case someDict g (Proxy :: Proxy g) of
Dict -> someFun g
有多个可能的代表。采用元数据自由,:+:
的两种可能表示是
data MySum = A | B | C
构造函数的表示可以是
MySum