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
的每次出现都显示为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中,目前没有办法为所有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的两种可能表示形式