【问题标题】:Having a custom fmap traversing until non Functor type is found遍历自定义 fmap 直到找到非 Functor 类型
【发布时间】:2017-11-13 15:28:14
【问题描述】:

在我的 Haskell 学习曲线中,我想知道是否可以实现自定义 fmap 函数 fmap' ,它会“应用”直到找到非 Functor。

例如;使用 fmap ,必须编写

fmap (fmap (*2)) [[1,2] , [ 3,4 ,5]]
[[2,4],[6,8,10]]

fmap (fmap (*2)) [Just 1, Just 2]
[Just 2,Just 4]

想法是写

fmap' (*2) [[1,2] , [3,4,5]]
fmap' (*2) [Just 1 , Just 2]

得到与上面相同的结果。

我目前的理解是不可能的,因为fmap'和fmap的签名不一样

:t fmap (fmap) 
fmap (fmap)
  :: (Functor f2, Functor f1) => f1 (a -> b) -> f1 (f2 a -> f2 b)

【问题讨论】:

  • 您应该将其与fmap . fmap 的类型进行比较(不是 fmap fmap)。 (另请参阅fmap . fmap . fmap 等的类型)。 -- 但我知道您希望fmap' 决定. 连接的链中有多少fmap,对吗?
  • 您希望有一个函数来确定任意类型是否具有Functor 实例,但这不可能——这根本不是类型类的工作方式。通常当人们想要做这样的事情时,这是因为他们希望在这里和那里节省一些打字;关于您想要实现的目标的一些背景会有所帮助。
  • @Wil Ness,是的,你明白我想问什么
  • @user2407038 :没有上下文,只是为了帮助我进步和理解haskell技巧;

标签: haskell


【解决方案1】:

你正在寻找一个函数:

rfmap :: (a -> b) -> F a -> F b

其中Fa 的一些任意“上下文”,这可能非常复杂。例如。一个专业是:

rfmap :: (a -> b) -> Maybe (Int -> Either Bool a) -> Maybe (Int -> Either Bool b)

我们需要类型检查器来找出F = Maybe (Int -> Either Bool _)。这不像fmap那么简单,它的参数是应用于变量的类型构造函数

fmap :: (a -> b) -> Compose Maybe (Either Bool) a -> Compose Maybe (Either Bool) b
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^      ^^^^^^^^^^^^^^^^^^^^^^^^^^^

而且很容易找到,因为类型构造函数是单射的。

rfmap 需要处理一些棘手的歧义。考虑:

rfmap id (Just [42])

我们应该选择哪个专业?

rfmap :: (Int -> Int) -> Maybe [Int] -> Maybe [Int]
rfmap :: ([Int] -> [Int]) -> Maybe [Int] -> Maybe [Int]
rfmap :: (Maybe [Int] -> Maybe [Int]) -> Maybe [Int] -> Maybe [Int]

也就是说,我们是否考虑函子Maybe _Maybe (Int -> _),甚至是恒等函子_?您可能想说“尽可能深”,但很可惜,这会干扰多态性:

f :: a -> Maybe a
f x = rfmap id (Just x)

您可能希望这只是一个fmap,因为更深入不会进行类型检查。但是,如果我们通过f [42],那么它与上面的表达式相同,它按照“尽可能深”的规则下降了两个——所以这有点像违反引用透明度(我不认为它是完整的违规,因为存在可能不同的隐藏类型导向信息)。

没关系,仿函数可能有 Num 实例,例如Num a => Num (Vector a),这意味着[42] 实际上更多超过两层深度。

所以有很多棘手的问题使得像 rfmap 这样的东西不太可能有一个良好的实现。

像往常一样,如果你放弃了多态性(这是一个糟糕的想法),you can hack something together with typeclasses。它还有一些其他的问题。

我建议不要期望任何解决方案都可用。这种东西与 Haskell 的类型系统不太兼容。

至于替代方案:

  • 如果您正在编写单个实例并且只是对所有 fmaps 感到恼火,请查看 semantic editor combinators 以了解一个很好的思考方式。
  • 如果您要进行抽象(在具有操作的类型的意义上),这些抽象只是嵌套的函子,并且对繁琐的 fmap = fmap.fmap.fmap 重新实现感到恼火,那么使用 Compose 是可行的方法。

【讨论】:

  • 能否请您说明如何将(*2) 映射到[[[1,2]], [[3],[4,5]]] 中的数字与Compose?我知道我可以通过 (fmap.fmap.fmap) 简单地做到这一点。
  • @WillNess,如果你只做一次,那并不是Compose 真正擅长的,但没关系。 getCompose . getCompose . fmap (*2) . Compose . Compose
  • 谢谢。我确定我尝试了类似的方法,但没有奏效。 :) 非常感谢!
猜你喜欢
  • 2012-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-21
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多