Haskell 函子/类似应用程序的类型类,用于编写(a->;a)函数而不是(a->;b)?

Haskell 函子/类似应用程序的类型类,用于编写(a->;a)函数而不是(a->;b)?,haskell,Haskell,我有这样一种类型: newtype Canonical Int = Canonical Int 和一个函数 canonicalize :: Int -> Canonical Int canonicalize = Canonical . (`mod` 10) -- or whatever (规范化类型可能并不重要,它只是用来区分“原始”值和“规范化”值。) 我想创建一些机制,以便规范化函数应用程序的结果 例如:(编辑:修复虚假定义) 这在表面上类似于函子和Applicative,

我有这样一种类型:

newtype Canonical Int = Canonical Int
和一个函数

canonicalize :: Int  -> Canonical  Int
canonicalize =  Canonical . (`mod` 10)  -- or whatever
(规范化类型可能并不重要,它只是用来区分“原始”值和“规范化”值。)

我想创建一些机制,以便规范化函数应用程序的结果

例如:(编辑:修复虚假定义)

这在表面上类似于函子和Applicative,但并不完全一样,因为它太专业化了:除非“b”是Int,否则我实际上无法编写函数(按照函子/Applicative同态定律的要求)

我的目标是使用现有的库函数/组合函数,而不是编写自己的变体,如
cmap
cmap2
。可能吗?是否有一种不同的类型类,或者一种不同的方法来构造规范类型,以实现我的目标

我试过其他结构,比如

newtype Canonical a = Canonical { value :: a, canonicalizer :: a -> a }
但这也会遇到同样的不可组合性问题,因为我无法将一个规范化程序转换为另一个规范化程序(我只想使用结果类型的规范化程序,它总是
Int
(或
Integral a

我不能强制“只专业化”这样,这是无效的Haskell:

instance (Functor Int) (Canonical Int) 
(和类似的变化)

我也试过了

newtype (Integral a) => Canonical a = Canonical a -- -XDatatypeContexts
instance (Integral a) => Functor Canonical where
  fmap f (Canonical x) = canonicalize  $ f x
但是GHC说,
DatatypeContexts
不受欢迎,这是一个坏主意,而且更严重, 我得到:

我想这是说约束
Integral a
实际上不能用于约束
fmap
(Integral->Integral)
我希望的方式,这有点明显(因为
fmap
有两个类型变量):-(

当然这也不是有效的Haskell

instance (Integer a) => Functor Canonical where

我是否可以使用类似的typeclass,或者我尝试使用typeclass来实现“隐式规范化函数调用结果”的功能是错误的?

从您的类型签名来看,这看起来有点像
标识
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)
然后你可以得到一个幺半群

instance Monoid Automorphism where
         mempty = id
         mappend = (.)
         mconcat = foldr1 (.)

(我希望foldr1是正确的)

您的
cmap
实际上可以通过
fmap
实现。一开始有点奇怪,但是
(>)
本身只是一个数据类型,实际上相当于
阅读器
。我们可以
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))
现在在代码中看到这通常有点奇怪,在
(>)


显然,这在高阶组合子中有点荒谬。无点样式可能不是最佳选择。

我认为您试图实现的功能在包中可用,在本例中是单函子类型类。

让我们后退一步。您确定类型签名
cmap2::(b->c->Int)->(规范Int)->(Canonical Int)
描述您想要什么?因为它看起来毫无意义。它也与
函子
无关。下面是一个有效函子签名的示例:
(Int->Int)->Canonical Int->Canonical Int
。我的错误是,我在将更复杂的类型简化为需要询问的内容时犯了错误。我(尝试过)改进定义,进行编辑。我同意它看起来有点像
Identity
,但如果我尝试将
canonicalize
放在定义的左侧,我认为同样的类型检查问题也适用。
Identity
Canonical
是参数多态的,但是
canonicalize
是特殊的polymo你能解释一下吗?我想我现在太聪明了(这种特殊的多态性让我的脑袋发麻)-也许明天我会想,我的意思是,
Identity
适用于任何类型的
a->a
,但是
canonicalize
被实现为
Int->Int
你的
自同构是
数据的
Endo
。Monoid
@misterbee:那就把
Identity
用于所有东西,而只使用canonicalizi呢必要时使用ng?那么您可以在大多数情况下使用Haskell的所有多态性,并且您只需要在绝对必要时使用规范化的
Int
。如果您另外定义了
Identity Int
Canonical Int
之间的同构,这种方法可能非常轻量级。这看起来令人鼓舞,但是请注意,我并不是简单地尝试用
Canonical
构造函数标记一个值。我还尝试添加一个转换:
canonicalize=Canonical。(`mod`10)
Ah,mono traversable的神奇之处在于它使用了“类型族”。这样,一个具体的类型
Text
可以被视为
Foo Char
。有趣的是,在
omap
中,
Char
被用作
元素Text
,签名是
(元素m->元素m)->m->m
这不完全是
(a->a->ma->ma
。从某种意义上说,
m
被视为
UnElement(Element m)
,要使类型签名匹配
fmap
。需要将mono遍历到fpcomplete;-)。它在不稳定的包集中可用。在我们创建9月份稳定的快照时,mono traversable不够稳定。啊,昨天我找不到汉堡包菜单来访问FPComplete IDE中的设置。切换到不稳定工作非常好!
cmap :: (a -> Int) -> a -> (Canonical Int)
cmap = fmap Can
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))
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