跨类型构造函数编写泛型仿函数实例?
Writing a generic functor instance across type constructors?
我正在学习基本类型 类 并且已经为我的类型 Test a
编写了自己的 functor
实现(表现就像 Maybe
):
data Test a = Test a | Emp
class FC c a where
t :: (a -> b) -> c a -> c b
instance FC Test a where
t f (Test a) = Test (f a)
t f (Emp) = Emp
instance FC Maybe a where
t f (Just a) = Just (f a)
t f (Nothing) = Nothing
是否可以实现类似的东西:
instance FC c where
t f (c v) = c (f v)
错误:
Parse error in pattern: c
换句话说,抽象出类型构造函数,替换为 c
和 v
,从而创建一个可以应用于具有上下文的任何值的通用实例?
据我所知,这是不可能的,因为可以有多个构造函数,并且不知道通用构造函数 Foo
是否可以将任何属性作为类型。
比如说你有一个类型叫做:
data Foo a = Bar Int | Qux a
现在这意味着你不能抽象出构造函数。只要是 Qux
就没有问题,但是 Bar
总是期待一个 Int
因此会出错。因为你在这里定义了一个 instance
在任何类型的 c
之上,所以在某些情况下这是行不通的。此外请注意,instance
声明中的 c
与 t
定义中的 c
无关。换句话说:构造函数可以隐含类型约束,因此您不能简单地将它们分解出来。
关于你的问题的评论是你可以概括你的 class
定义和 instance
:
class FC c where
t :: (a -> b) -> c a -> c b
instance FC Test where
t f (Test a) = Test (f a)
t f Emp = Emp
因此您可以删除 class
定义中的 a
。这不等同于您的问题,因为您在这里说它适用于任何 a
。而当您定义 class FC c a
时,您可以决定要为哪些 a
实施 instance
.
如您所知,c a
不是语法上有效的模式。但是,请将您的问题作为功能提案阅读:那将如何工作?并非每个 Functor
都有一个 single-element 构造函数,可以根据您的模式进行映射。一些例子:
data Pair a = Pair a a -- more than one element
instance Functor Pair where
fmap f (Pair x y) = Pair (f x) (f y)
data Proxy a = Proxy -- no elements
instance Functor Proxy where
fmap f Proxy = Proxy
newtype Cont r a = Cont { runCont :: (a -> r) -> r } -- element appears in a double-negative position
instance Functor (Cont r) where
fmap f (Cont g) = Cont (g . (. f))
无论如何,我认为 "generic instance" 的想法没有任何意义。该实例是您放置 type-specific 代码的地方。 (它必须去某个地方!)
如果您想在编写 Functor
实例时付出更少的努力,您可以使用 GHC 的 DeriveFunctor
扩展。
{-# LANGUAGE DeriveFunctor #-}
data Pair a = Pair a a deriving Functor
data Proxy a = Proxy deriving Functor
newtype Cont r a = Cont { runCont :: (a -> r) -> r } deriving Functor
您可以使用 GHC.Generic
做一些非常通用的事情。这是通用 FC
class 定义的不完整示例(这正是 generic-deriving 包所做的):
首先进行一些扩展并导入泛型机制
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeOperators #-}
import GHC.Generics
然后我们定义一个 class 来反映您的 FC
但我们只有通用类型的实例
class GFC c where
gt :: (a -> b) -> c a -> c b
-- Constructors without arguments (Empty)
instance GFC U1 where
gt _ U1 = U1
-- Constructors where the parameter appears (Test a)
instance GFC Par1 where
gt f (Par1 a) = Par1 (f a)
-- Sums (| in datatype definitions)
instance (GFC f, GFC g) => GFC (f :+: g) where
gt f (L1 a) = L1 (gt f a)
gt f (R1 a) = R1 (gt f a)
-- Meta information wrapper
instance GFC f => GFC (M1 i c f) where
gt f (M1 a) = M1 (gt f a)
-- ... the rest of the instances for the generic types here.
-- But these 4 instances are all that is needed for your `Test` type.
那么就可以根据上面的"generic"FC
:
对FC
进行默认实现
class FC c where
t :: (a -> b) -> c a -> c b
default -- DefaultSignatures allows us to do this
t :: (Generic1 c, GFC (Rep1 c)) => (a -> b) -> c a -> c b
t f = to1 . gt f . from1
-- turn something with Generic1 into its generic representation,
-- use the generic `gt` and then turn it back into its actual
-- representation
data Test a = Test a | Empty
deriving (Generic1, Show)
instance FC Test
有效:
GHCI> t (==0) (Test (1 :: Int))
Test False
我正在学习基本类型 类 并且已经为我的类型 Test a
编写了自己的 functor
实现(表现就像 Maybe
):
data Test a = Test a | Emp
class FC c a where
t :: (a -> b) -> c a -> c b
instance FC Test a where
t f (Test a) = Test (f a)
t f (Emp) = Emp
instance FC Maybe a where
t f (Just a) = Just (f a)
t f (Nothing) = Nothing
是否可以实现类似的东西:
instance FC c where
t f (c v) = c (f v)
错误:
Parse error in pattern: c
换句话说,抽象出类型构造函数,替换为 c
和 v
,从而创建一个可以应用于具有上下文的任何值的通用实例?
据我所知,这是不可能的,因为可以有多个构造函数,并且不知道通用构造函数 Foo
是否可以将任何属性作为类型。
比如说你有一个类型叫做:
data Foo a = Bar Int | Qux a
现在这意味着你不能抽象出构造函数。只要是 Qux
就没有问题,但是 Bar
总是期待一个 Int
因此会出错。因为你在这里定义了一个 instance
在任何类型的 c
之上,所以在某些情况下这是行不通的。此外请注意,instance
声明中的 c
与 t
定义中的 c
无关。换句话说:构造函数可以隐含类型约束,因此您不能简单地将它们分解出来。
关于你的问题的评论是你可以概括你的 class
定义和 instance
:
class FC c where
t :: (a -> b) -> c a -> c b
instance FC Test where
t f (Test a) = Test (f a)
t f Emp = Emp
因此您可以删除 class
定义中的 a
。这不等同于您的问题,因为您在这里说它适用于任何 a
。而当您定义 class FC c a
时,您可以决定要为哪些 a
实施 instance
.
如您所知,c a
不是语法上有效的模式。但是,请将您的问题作为功能提案阅读:那将如何工作?并非每个 Functor
都有一个 single-element 构造函数,可以根据您的模式进行映射。一些例子:
data Pair a = Pair a a -- more than one element
instance Functor Pair where
fmap f (Pair x y) = Pair (f x) (f y)
data Proxy a = Proxy -- no elements
instance Functor Proxy where
fmap f Proxy = Proxy
newtype Cont r a = Cont { runCont :: (a -> r) -> r } -- element appears in a double-negative position
instance Functor (Cont r) where
fmap f (Cont g) = Cont (g . (. f))
无论如何,我认为 "generic instance" 的想法没有任何意义。该实例是您放置 type-specific 代码的地方。 (它必须去某个地方!)
如果您想在编写 Functor
实例时付出更少的努力,您可以使用 GHC 的 DeriveFunctor
扩展。
{-# LANGUAGE DeriveFunctor #-}
data Pair a = Pair a a deriving Functor
data Proxy a = Proxy deriving Functor
newtype Cont r a = Cont { runCont :: (a -> r) -> r } deriving Functor
您可以使用 GHC.Generic
做一些非常通用的事情。这是通用 FC
class 定义的不完整示例(这正是 generic-deriving 包所做的):
首先进行一些扩展并导入泛型机制
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeOperators #-}
import GHC.Generics
然后我们定义一个 class 来反映您的 FC
但我们只有通用类型的实例
class GFC c where
gt :: (a -> b) -> c a -> c b
-- Constructors without arguments (Empty)
instance GFC U1 where
gt _ U1 = U1
-- Constructors where the parameter appears (Test a)
instance GFC Par1 where
gt f (Par1 a) = Par1 (f a)
-- Sums (| in datatype definitions)
instance (GFC f, GFC g) => GFC (f :+: g) where
gt f (L1 a) = L1 (gt f a)
gt f (R1 a) = R1 (gt f a)
-- Meta information wrapper
instance GFC f => GFC (M1 i c f) where
gt f (M1 a) = M1 (gt f a)
-- ... the rest of the instances for the generic types here.
-- But these 4 instances are all that is needed for your `Test` type.
那么就可以根据上面的"generic"FC
:
FC
进行默认实现
class FC c where
t :: (a -> b) -> c a -> c b
default -- DefaultSignatures allows us to do this
t :: (Generic1 c, GFC (Rep1 c)) => (a -> b) -> c a -> c b
t f = to1 . gt f . from1
-- turn something with Generic1 into its generic representation,
-- use the generic `gt` and then turn it back into its actual
-- representation
data Test a = Test a | Empty
deriving (Generic1, Show)
instance FC Test
有效:
GHCI> t (==0) (Test (1 :: Int))
Test False