我的类型如下:
newtype Canonical Int = Canonical Int
和一个功能
canonicalize :: Int -> Canonical Int
canonicalize = Canonical . (`mod` 10) -- or whatever
(Canonical类型可能并不重要,它只是用于区分“原始”值和“规范化”值。)
我想创建一些机器,以便我可以规范化函数应用程序的结果。
例如:(编辑:固定虚假定义)
cmap :: (b->Int) -> (Canonical b) -> (Canonical Int)
cmap f (Canonical x) = canonicalize $ f x
cmap2 :: (b->c->Int) -> (Canonical b) -> (Canonical c) -> (Canonical Int)
cmap2 f (Canonical x) (Canonical y) = canonicalize $ f x y
表面上与Functor和Applicative类似,但它并不完全,因为它太专业了:我实际上不能编写函数(按照Functor / Applicative的同态定律的要求),除非'b'是Int。< / p>
我的目标是使用现有的库函数/组合器,而不是编写我自己的变体,如cmap
,cmap2
。那可能吗?是否有不同的类型类或构造Canonical类型的不同方式来实现我的目标?
我尝试过其他结构,比如
newtype Canonical a = Canonical { value :: a, canonicalizer :: a -> a }
但是遇到了相同的非可组合性问题,因为我无法将一个规范化器转换为另一个规范化器(我只想使用结果类型的规范化器,它总是Int
(或{{1} })
我不能强迫“仅限专业化”,这不是有效的Haskell:
Integral a
(和类似的变化)
我也试过
instance (Functor Int) (Canonical Int)
但GHC表示newtype (Integral a) => Canonical a = Canonical a -- -XDatatypeContexts
instance (Integral a) => Functor Canonical where
fmap f (Canonical x) = canonicalize $ f x
已被弃用,这是一个坏主意,更严重的是,
我明白了:
DatatypeContexts
我认为约束 `Could not deduce (Integral a1) arising from a use of 'C'
from the context (Integral a)
bound by the instance declaration
[...] fmap :: (a1 -> b) -> (C a1 -> C b)
实际上不能用于将Integral a
约束到fmap
我想要的方式,这很明显(因为{{1}有两个类型变量): - (
当然这不是有效的Haskell
(Integral -> Integral)
是否有类似的类型类我可以使用,或者我是否错误地试图使用类型类来完成“隐式规范化函数调用结果”的功能?
答案 0 :(得分:5)
我认为您尝试实现的目标可以在mono-traversable包中找到,在本例中是MonoFunctor类型类。
答案 1 :(得分:2)
看起来有点像Identity
monad
newtype Identity a = Identity a
instance Monad Identity where
return x = Identity x
f >>= (Identity x) = Identity (f x)
然后在您的示例canonicalize = return
和cmap f x = f >>= (return x)
希望这有用
我想到的另一个结构是自同构(我研究过数学)
所以,如果你有
data Automorphisms a = AMorph (a -> a)
然后你可以有一个Monoid
instance Monoid Automorphism where
mempty = id
mappend = (.)
mconcat = foldr1 (.)
(我希望foldr1是正确的)
答案 2 :(得分:1)
您的cmap
实际上可以通过fmap
实现。起初有点奇怪,但(->)
本身只是一种数据类型,实际上等同于Reader
。我们可以fmap
超过返回结果。
cmap :: (a -> Int) -> a -> (Canonical Int)
cmap = fmap Can
您可以使用相同的模式
创建其他cmap
变体
cmap2 :: (a -> b -> Int) -> a -> b -> (Canonical Int)
cmap2 = fmap (fmap Can)
cmap3 :: (a -> b -> c -> Int) -> a -> b -> c -> (Canonical Int)
cmap3 = fmap (fmap (fmap Can))
现在通常在代码中看起来有些奇怪,在fmap
(->)
的一般形式更为常见
instance Functor (r ->) where
fmap = (.)
cmap :: (a -> Int) -> a -> (Canonical Int)
cmap = (.) Can
cmap f = Can . f
cmap2 :: (a -> b -> Int) -> a -> b -> (Canonical Int)
cmap2 = (.) ((.) Can)
cmap2 = (.) (Can .)
cmap2 f = (Can .) . f
cmap3 :: (a -> b -> c -> Int) -> a -> b -> c -> (Canonical Int)
cmap3 = (.) ((.) ((.) Can))
cmap3 = (.) ((.) (Can .))
cmap3 = (.) ((Can .) .)
cmap3 f = ((Can .) .) . f
显然,在高阶组合器中,这有点荒谬。 Pointfree风格可能不是最佳选择。