用于编写(a-> a)函数的Functor / Applicative-like类型类而不是(a-> b)?

时间:2013-12-25 23:55:49

标签: haskell

我的类型如下:

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>

我的目标是使用现有的库函数/组合器,而不是编写我自己的变体,如cmapcmap2。那可能吗?是否有不同的类型类或构造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)

是否有类似的类型类我可以使用,或者我是否错误地试图使用类型类来完成“隐式规范化函数调用结果”的功能?

3 个答案:

答案 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 = returncmap 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风格可能不是最佳选择。

相关问题