Haskell 如何在(:+;:)f g p中为p指定类约束?(带:+;:来自GHC.Generics)
对构造函数之间的选择进行编码。泛型定义以下类型:Haskell 如何在(:+;:)f g p中为p指定类约束?(带:+;:来自GHC.Generics),haskell,generics,types,Haskell,Generics,Types,对构造函数之间的选择进行编码。泛型定义以下类型: data (:+:) f g p = L1 (f p) | R1 (g p) 泛型类提供了将泛型类型转换为表示的方法: from :: a -> Rep a x class MyClass r where myFun :: r a -> Maybe Int 为了编写泛型函数,我定义了一个用于表示的类: from :: a -> Rep a x class MyClass r where myFun :: r a -
data (:+:) f g p = L1 (f p) | R1 (g p)
泛型类提供了将泛型类型转换为表示的方法:
from :: a -> Rep a x
class MyClass r where myFun :: r a -> Maybe Int
为了编写泛型函数,我定义了一个用于表示的类:
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
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’
如何将SomeClass约束添加到泛型sum类型的MyClass实例?换句话说,以下实例有什么问题:
instance (SomeClass (f p),SomeClass (g p)) => SomeClass ((:+:) f g p) where
someFun (R1 _) = Just 42
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’
如果您将
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(可能是Int)
实例
-- composition of functors
instance (MyClass f, MyClass g, Functor f) => MyClass (f :.: g) where
myFun (Comp1 fg) = myFun $ fmap myFun fg
-- The following instances are needed for the composition of functors
instance SomeClass Int where
someFun = Just
instance SomeClass a => SomeClass (Maybe a)
从MyClass派生SomeClass
我们将得到SomeClass
实例,这些实例一般重用MyClass
来获得它们。由于MyClass
的myFun
需要一个SomeClass
实例,我们需要证明参数Par1
从未出现在Rep
中<“中的code>将证明参数为空
class SomeClass a where
someFun :: a -> Maybe Int
default someFun :: (Generic a, MyClass (Rep a)) => a -> Maybe Int
someFun a = myFun (from' a)
来自的Void
类型表示逻辑上不存在的类型。下面证明了泛型
的参数始终为空
-- Prove that the parameter is always empty
from' :: Generic a => a -> Rep a Void
from' = from
为了满足SomeClass
对myFun
的约束,我们为Void
配置了一个SomeClass
实例。我们可以确保永远不会调用someFun::Void->Maybe Int
,因为没有类型为Void
的值传递给它
instance SomeClass Void where
someFun = absurd
现在我们可以为SomeClass(可能是Int)
派生一个实例,假设我们有一个SomeClass Int
实例
-- composition of functors
instance (MyClass f, MyClass g, Functor f) => MyClass (f :.: g) where
myFun (Comp1 fg) = myFun $ fmap myFun fg
-- 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
除了Par1
和Rec1
之外,您可以为所有对象编写GSomeClass
实例,并使用GSomeClass
派生SomeClass
实例。Generic
实例从不使用该参数,即使是像可能是a
这样的类型也不使用该参数;相反,参数a
的每次出现都显示为aK1 i a p
class SomeClass a where
someFun :: a -> Maybe Int
default someFun :: (Generic a, GSomeClass (Rep a)) => a -> Maybe Int
someFun a = gsomeFun (from a)
在Haskell中,目前没有办法为所有p编写类似的约束。SomeClass(fp)=>这是您在类型中尝试执行的操作
(SomeClass (f p), SomeClass (g p), MyClass f, MyClass g) => MyClass ((:+:) f g)
有一个技巧可以用第二个类型类捕获这些约束。您可以编写第二个类FSomeClass f
,表示所有p的。SomeClass(fp)
。如果SomeClass
很简单,您可以在FSomeClass
的字典中,使用fa
代替a
到处复制SomeClass
的字典字段
class FSomeClass f where
fsomeFun :: f a -> Maybe Int
如果SomeClass
更复杂,或者如果需要为依赖它的代码提供一个真正的SomeClass
实例,则可以在GADT
中捕获整个SomeClass
字典Dict
捕获任何约束的字典
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ConstraintKinds #-}
data Dict c where
Dict :: c => Dict c
使用Dict
s,我们可以编写这样一个类:对于每个a
,fa
都有一个SomeClass
实例被困在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
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(fa)
实例,您需要在字典上进行模式匹配
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和FSomeClass实例
我们将为GHC.Generics
和FSomeClass f
中的所有类型创建SomeClass(fp)
实例,以证明p
可以在所有类型中变化
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
对于Par1
,我们无法使用参数p
,因为它可以在所有类型中变化。我们唯一的其他选择是不提供SomeClass(par1p)
instance。对于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
M1
和Rec1
的SomeClass
实例需要SomeClass(fp)=>
约束。在为例如SomeClass(Rec1 f p)
构建Dict之前,我们必须为SomeClass(f p)
引入一个字典。我们从f
的FSomeClass
实例中获取字典,并在其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
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
使用FSomeClass
使用原始的MyClass
class MyClass f where
myFun :: f a -> Maybe Int
我们将为:+:
编写一个实例。我们将接受您的原始签名,并将SomeClass(fp)
替换为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的两种可能表示形式