Haskell “哈斯克尔”;伪函子;

Haskell “哈斯克尔”;伪函子;,haskell,functor,Haskell,Functor,我有一个多项式 data Poly a = Poly [a] 我希望能够做一些类似于fmap(take 3)多项式的事情,但是我不能,因为Poly不是真正的函子,因为fmap中使用的f只能是[a]->[b]类型,而不是a->b 有没有一个成语或方式可以表达我想要的 编辑:这里有一个函数,它实现了我想要的功能 myMap :: ([a] ->[b]) -> P a -> P b myMap f (P x) = P (f x) 用法: *Main> myMap (tak

我有一个多项式

data Poly a = Poly [a]
我希望能够做一些类似于
fmap(take 3)多项式的事情,但是我不能,因为
Poly
不是真正的函子,因为
fmap
中使用的
f
只能是
[a]->[b]
类型,而不是
a->b

有没有一个成语或方式可以表达我想要的


编辑:这里有一个函数,它实现了我想要的功能

myMap :: ([a] ->[b]) -> P a -> P b
myMap f (P x) = P (f x)
用法:

*Main> myMap (take 3) (P [1..])
P [1,2,3]

从sig类型可以看出,它几乎是fmap,但不完全是。很明显,我能够为
myMap
编写代码,但我只想知道是否还有另一个习惯用法应该使用。

这不起作用,但我认为它足够有趣,可以与大家分享:

{-#LANGUAGE GADTs #-}

data Poly a where
   Poly :: [b] -> Poly [b]
我们现在有一个类型Poly,它在
a
上参数化,但实际上
a
必须是一个列表:

~% ghci Poly.hs
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
[1 of 1] Compiling Main             ( Poly.hs, interpreted )
Ok, modules loaded: Main.
*Main> :k Poly
Poly :: * -> *
*Main> :t Poly
Poly :: [b] -> Poly [b]
*Main> case Poly [1,2,3] of _ -> 0
0
*Main> case Poly 4 of _ -> 0

<interactive>:1:10:
    No instance for (Num [b])
      arising from the literal `4' at <interactive>:1:10
    Possible fix: add an instance declaration for (Num [b])
    In the first argument of `Poly', namely `4'
    In the scrutinee of a case expression: Poly 4
    In the expression: case Poly 4 of _ -> 0
*Main> case Poly True of _ -> 0

<interactive>:1:10:
    Couldn't match expected type `[b]' against inferred type `Bool'
    In the first argument of `Poly', namely `True'
    In the scrutinee of a case expression: Poly True
    In the expression: case Poly True of _ -> 0
那是行不通的。有趣的是,我们甚至不能真正编写
myMap

polymap f (Poly x) = Poly (f x)
如果我们尝试一下,我们会得到

GADT pattern match in non-rigid context for `Poly'
  Tell GHC HQ if you'd like this to unify the context
In the pattern: Poly x
In the definition of `polymap': polymap f (Poly x) = Poly (f x)
当然,我们可以使用类型注释修复它:

 polymap :: ([a] -> [b]) -> Poly [a] -> Poly [b]

但如果没有它,这是一个类似于fmap的问题。Functor没有任何地方可以解释“我保证永远使用列表”这一额外的上下文,事实上它也不能。例如,您总是可以说
undefined::Poly Int
。简言之,我不认为真的有一个成语可以表达这一点(实际上,有人可能会提供足够的ghc扩展魔法来实现这一点)。当然不是现有的。

因为您允许对系数列表应用任何函数,所以您的数据类型实际上只有两个用途

  • 由于
    Poly[a]
    不同于
    [a]
    ,因此您可以获得额外的类型安全性
  • 您可以定义不同的实例
如果您不需要这两者中的任何一个,那么最好使用类型别名

type Poly a = [a]
现在您可以直接在其上应用任何列表函数

另一方面,如果您想要一个独特的类型,您可能会发现它很有用。例如,给定此实例

instance Newtype (Poly a) [a] where
  pack = Poly
  unpack (Poly x) = x
你现在可以写这样的东西了

foo :: Poly a -> Poly a
foo = over Poly (take 3)
尽管如果您的
myMap
足以满足您的需要,这可能会有点过头


除此之外,我认为以这种方式公开数据类型的表示可能不是一个好主意,因为它会使代码的其余部分密切依赖于这种表示

这使得以后更难更改为不同的表示形式。例如,您可能希望更改为稀疏表示,如

data Poly a = Poly [(a, Int)]
其中,
Int
是术语的幂。我建议你考虑一下你想暴露什么样的操作,并将自己局限于这些操作。例如,有一个基于每个元素的
Functor
实例可能是有意义的

instance Functor Poly where
    fmap f (Poly x) = Poly $ map f x
现在,对稀疏表示的更改使客户机代码保持不变。只有实例(以及少数依赖于表示的其他函数)需要更改

instance Functor Poly where
    fmap f (Poly x) = Poly $ map (first f) x

您希望
fmap(take 3)多项式
做什么?这对我来说毫无意义
多项式
只是一个
多项式::Poly CoeffType
多项式=Poly[a₁, A.₂ ..]
?@leftaroundabout:我已经给出了一些示例代码。由于这不是一个真正的规范操作(typeclass如何知道它是一个列表而不是一个数组),我不会将其作为任何实例。我也不会将其称为“some-
map
”而是
coeffstrasform
或类似的东西。谢谢,它看起来像是按照您指定的方式使用fmap。
instance Functor Poly where
    fmap f (Poly x) = Poly $ map (first f) x