为了对构造函数之间的选择进行编码,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’
最佳答案
如果您将 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
从 MyClass 派生 SomeClass
我们将获得 SomeClass
实例,一般重用 MyClass
来获得它们。由于 MyClass
的 myFun
需要一个 SomeClass
实例,我们需要证明参数 Par1
永远不会出现在 Rep
中。 from'
将证明参数为空。
class SomeClass a where
someFun :: a -> Maybe Int
default someFun :: (Generic a, MyClass (Rep a)) => a -> Maybe Int
someFun a = myFun (from' a)
void 中的 Void
类型表示逻辑上不存在的类型。以下证明 Generic
的参数始终为空
-- Prove that the parameter is always empty
from' :: Generic a => a -> Rep a Void
from' = from
为了满足 myFun
的 SomeClass
约束,我们为 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
您将为除 Par1
和 Rec1
之外的所有内容编写 GSomeClass
实例,并使用 GSomeClass
派生 SomeClass
实例。 Generic
实例从不使用该参数,甚至对于像 Maybe a
这样的类型也不使用;相反,参数 a
的每次出现都显示为 K1 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 - 如何在 ( :+:) f g p? (With :+: from GHC. Generics) 中为 p 指定类约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32711316/