让Haskell仿函数沉入。 [英] Let Haskell functors sink in.

查看:91
本文介绍了让Haskell仿函数沉入。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

学习你一个Haskell 有一个关于仿函数的例子。我可以阅读LYAH和文本,并找出应该发生的事情 - 但我不知道写这样的东西。我在Haskell中经常遇到这个问题。

 实例Functor(或者a)其中
fmap f(Right x)= Right(fx)
fmap f(Left x)= Left x

然而,我很困惑..为什么不($ x

 实例Functor(或者a)其中
fmap f(Right x)= Right(x)
fmap f(Left x)= Left(fx)

如果 f 没有被用在顶层定义中,那么还有什么限制 x ,使得它不能满足 Left

解决方案

以下是函子类:

  class Functor f其中
fmap ::(a - > b) - > f a - > fb

请注意,f本身是一个类型构造函数,因为它应用于fmap线。这里有一些例子可以说明这一点:



类型构造函数:

  IO 
也许
无论是字符串

类型:

  IO Char 
也许
要么是字符串字符串

也许a是一种带有一个类型构造函数(Maybe)和一个类型变量(a)的类型。它不是具体的东西,但它可用于多态函数的类型签名。



或者是一个带有两个类型参数的类型构造函数,所以即使在应用一个(例如>或者String 它仍然是一个类型构造函数,因为它可以接受另一个类型实参。



是:在定义 Functor 实例时,类型构造函数 f 不能更改。是因为它是由相同的变量 f 表示的,它既是 fmap 的参数和结果。允许改变的是应用于 f 构造函数的类型。



当你写实例Functor(或c),> f 中填充了c 声明 fmap 。这给了fmap以下类型的实例:

  fmap ::(a  - > b) - >(Eithe r c)a  - > (或者c)b 

定义或者,获得这种类型的唯一有用的方法是将 Right 值应用于该函数。请记住,两者有两种可能的值,可能有不同的类型。这里 Left 值的类型为'c',所以你不能将它应用到函数中(它需要'a')[1],结果也不会't是正确的,因为你将留下或者ba ,这与类定义不匹配。



将f替换为E或c以将fmap的上述类型签名替换为E c实例后,接下来写入实现。有两种情况需要考虑,左边和右边。类型签名告诉我们左侧的类型c不能改变。我们也没有办法改变价值,因为我们不知道它的实际类型。我们所能做的就是放弃它:

  fmap f(左rval)=左rval 

对于右侧,类型签名表示我们必须从类型为a的值更改为类型为b的值。第一个参数是完成这个功能的函数,所以我们使用带有输入值的函数来获取新的输出。把它们放在一起给出完整的定义

  instance Functor(或者c)其中
fmap f(Right rval)=右(f rval)
fmap f(左lval)=左lval

还有更多在这里工作的一般原则,这就是为什么编写一个调整左侧的Functor实例是不可能的,至少在Prelude定义中是如此。从上面复制一些代码:

  class Functor f其中
fmap ::(a - > b) - > ; f a - > fb

实例Functor(或c)其中...

尽管我们在实例定义中有一个类型变量'c',我们不能在任何类方法中使用它,因为它在类定义中没有提及。所以你不能写

  leftMap ::(c  - > d) - >或者c a  - >或者da 
leftMap mapfunc(Left x)= Left(mapfunc x)
leftMap mapfunc(Right x)= Right x

实例Functor(或者c)其中
--fmap ::(c - > d) - >或者c a - >或者da
fmap = leftMap

现在leftMap和fmap的结果是(或者d)a (任一c)已更改为(或d),但这是不允许的,因为没有办法在Functor课堂上表达它。为了表达这一点,你需要一个带有两个类型变量的类,例如:b
$ b

  class BiFunctor f where 
lMap: :(a - > b) - > f a c - > f b c
rMap ::(c - > d) - > f a c - > f a d
biMap ::(a - > b) - > (c - > d) - > f a c - > fbd

在这个类中,由于左右类型的变量都在范围之内,所以可以写(或双方)操作的方法。

  instance BiFunctor其中
lMap = leftMap
rMap = rightMap - 与标准fmap定义相同
biMap fl fr e = rMap fr(lMap fl e)

虽然在实践中,人们通常只需为BiFunctor类编写biMap,如果需要左映射或右映射,则使用id作为其他函数。


$ b $更准确地说,左值的类型'c',该函数需要'a',但类型检查器不能统一这些类型,因为'c'类型不在类定义。


Learn You a Haskell has an example about functors. I can read LYAH, and text, and figure out what is supposed to happen -- but I don't know enough to write something like this. I'm finding this problem often in Haskell.

instance Functor (Either a) where  
    fmap f (Right x) = Right (f x)  
    fmap f (Left x) = Left x

However, I'm confused.. Why doesn't this comple

instance Functor (Either a) where  
    fmap f (Right x) = Right (x)  
    fmap f (Left x) = Left (f x)

If f isn't being used in the top definition, then what else constrains x such that it can't satisfy Left

解决方案

Here's the functor class:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

Note that "f" by itself is a type constructor because it's applied to a type variable in the fmap line. Here are some examples to make this clear:

Type constructors:

IO
Maybe
Either String

Types:

IO Char
Maybe a
Either String String

"Maybe a" is a type with one type constructor (the "Maybe") and one type variable (the "a"). It's not something concrete yet, but it is usable in type signatures for polymorphic functions.

"Either" is a type constructor that takes two type arguments, so even after you apply one (e.g. Either String it's still a type constructor because it can take another type argument.

The point of this is: when you define a Functor instance, the type constructor f cannot change. This is because it's represented by the same variable, f, as both the argument and result of fmap. The only type that's allowed to change is the type that's applied to the f constructor.

When you write instance Functor (Either c), Either c is filled in for f everywhere in the declaration of fmap. This gives fmap the following type for this instance:

fmap :: (a -> b) -> (Either c) a -> (Either c) b

With the definition of Either, the only useful way to get this type is by applying the Right value to the function. Remember that "Either" has two possible values with possibly different types. Here the Left value has type 'c', so you can't apply it to the function (which expects an 'a')[1], and the result also wouldn't be correct because you'd be left with Either b a, which doesn't match the class definition.

After replacing "f" with "Either c" to get the above type signature for fmap with the "Either c" instance, writing the implementation is next. There are two cases to consider, the Left and the Right. The type signature tells us that the type of the Left side, "c", can't change. We also don't have any way to change the value because we don't know what type it actually is. All we can do is leave it alone:

fmap f (Left rval) = Left rval

For the Right side, the type signature says that we have to change from a value with type "a" to a value with type "b". The first argument is a function to do exactly that, so we use the function with the input value to get the new output. Putting the two together gives the full definition

instance Functor (Either c) where
  fmap f (Right rval) = Right (f rval)
  fmap f (Left lval) = Left lval

There's a more general principle at work here which is why writing a Functor instance that adjusts the Left side is impossible, at least with the Prelude definitions. Copying some code from above:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

instance Functor (Either c) where ...

Even though we have a type variable 'c' in the instance definition, we can't use it in any of the class methods because it's not mentioned in the class definition. So you can't write

leftMap :: (c -> d) -> Either c a -> Either d a
leftMap mapfunc (Left x) = Left (mapfunc x)
leftMap mapfunc (Right x) = Right x

instance Functor (Either c) where
  --fmap :: (c -> d) -> Either c a -> Either d a
  fmap = leftMap

The result of leftMap, and thus fmap, is now (Either d) a. The (Either c) has changed to an (Either d), but this isn't allowed because there's no way to express it in the Functor class. To express this, you'd need a class with two type variables, e.g.

class BiFunctor f where
  lMap :: (a -> b) -> f a c -> f b c
  rMap :: (c -> d) -> f a c -> f a d
  biMap :: (a -> b) -> (c -> d) -> f a c -> f b d

In this class, since both the left and right type variables are in scope, it's possible to write methods that operate on either (or both) sides.

instance BiFunctor Either where
  lMap = leftMap
  rMap = rightMap  --the same as the standard fmap definition
  biMap fl fr e = rMap fr (lMap fl e)

Although in practice people usually just write "biMap" for the BiFunctor class and use "id" for the other function if a left or right mapping is necessary.

[1] More accurately, the Left value has type 'c', the function expects an 'a', but the type checker can't unify those types because the 'c' type isn't in scope in the class definition.

这篇关于让Haskell仿函数沉入。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆