我有两个几乎相同的函数用于不同的数据构造函数,并想知道是否有可能统一两者。一个简约的例子是以下
f_maybe :: Maybe a -> a -> a
f_maybe (Just x) _ = x
f_maybe _ x = x
和
data T a = T1 a | T2 Int | T3
f_t :: T a -> a -> a
f_t (T1 x) _ = x
f_t _ x = x
这种方法只能定义一个由类型构造函数(Maybe或T)和数据构造函数(Just或T1)参数化的函数吗?
答案 0 :(得分:6)
为数据类型实施Foldable
实例后,此提示可以表示为foldr const
。
data T a = T1 a | T2 Int | T3
instance Foldable T where
-- if it's T1, it has a value
foldMap f (T1 a) = f a
-- if it's T2 or T3, it's considered "empty"
foldMap f (T2 _) = mempty
foldMap f T3 = mempty
您甚至可以让generic-deriving
个软件包为您推导出上述Foldable
个实例:
{-# LANGUAGE DeriveGeneric #-}
import Generics.Deriving.Foldable
import GHC.Generics
data T a = T1 a | T2 Int | T3
deriving (Generic1)
instance Foldable T where
foldMap = gfoldMapdefault
(在您的情况下,实例不含糊,因为只有一个构造函数可能只包含一个a
类型的值。如果数据类型过于复杂,多个构造函数包含一个或多个{{1} },你最好手动完成它或仔细检查那个软件包派生的内容)
ghci的一些结果:
a
答案 1 :(得分:3)
为了满足您的需求,两种类型都需要通过类型类共享一个公共接口。可以这样做:
class MyF mf where
f :: mf a -> a -> a
instance MyF Maybe where
f (Just x) _ = x
f _ x = x
instance MyF T where
f (T1 x) _ = x
f _ x = x
示例:
f (Just 3) 3
3
f Nothing "Hello"
"Hello"
f (T1 5) 3
5
f (T2 20) 2
2
答案 2 :(得分:3)
您可以编写一个统一的函数,它具有您想要的任何行为并且可以在任何行为上运行 参数类型,但如果没有具体数据,则无法进行模式匹配 构造函数(多态),AFAIK。
问问自己这个统一功能的类型签名是什么样的?
类型签名代表功能背后的想法。 Maybe
和T
有哪些内容
在这种情况下有共同点,这样你就可以统一起作用的功能
他们?你想要建立什么样的抽象?
所有这些事情都不清楚你的问题。如果你的想法是公正的 切割样板(这不是样板),然后不要尝试 统一不同的东西只是为了缩短代码,你会得到 最终令人困惑的事情。
类型实际应该是不同的,函数应该拒绝参数 错误的类型,不适用于任何看起来相似的东西。太 多态代码通常不是一个好主意,因为错误的代码可能会发生 对于类型检查器有一些意义,然后你会得到程序 编译,但做错了。