Haskell 组合映射的多态递归导致无限类型错误

Haskell Polymorphic Recursion with Composed Maps causes Infinite Type Error

创建可以动态创建组合地图的函数的正确方法是什么?

这会导致错误(也发生在 fmap 中):

createComposedMaps list = accumulate list map
    where 
        accumulate (x:xs) m = accumulate xs (m.map)
        accumulate []     m = m

list无关,只是统计作文的数量

我得到的错误是关于 "cannot construct infinite type":

Occurs check: cannot construct the infinite type: a2 ~ [a2]
Expected type: (a2 -> b1) -> a2 -> b1
  Actual type: (a2 -> b1) -> [a2] -> [b1]
Relevant bindings include
  m :: (a2 -> b1) -> c (bound at dimensional_filter.hs:166:27)
  accumulate :: [t1] -> ((a2 -> b1) -> c) -> (a2 -> b1) -> c
    (bound at dimensional_filter.hs:166:9)
In the second argument of ‘(.)’, namely ‘map’
In the second argument of ‘accumulate’, namely ‘(m . map)’

Occurs check: cannot construct the infinite type: b1 ~ [b1]
Expected type: (a2 -> b1) -> a2 -> b1
  Actual type: (a2 -> b1) -> [a2] -> [b1]
Relevant bindings include
  m :: (a2 -> b1) -> c (bound at dimensional_filter.hs:166:27)
  accumulate :: [t1] -> ((a2 -> b1) -> c) -> (a2 -> b1) -> c
    (bound at dimensional_filter.hs:166:9)
In the second argument of ‘(.)’, namely ‘map’
In the second argument of ‘accumulate’, namely ‘(m . map)’

目标是创建一个我以后可以使用的动态组合映射函数。而其他半群可以附加在一起(列表,数字......)。函数好像难多了

希望有一个显示 fmap and/or 地图的示例。

我找到了这个Library function to compose a function with itself n times

但我需要使用每个中间构图,而不仅仅是最终构图。一些例子仍然给我无限的类型错误。

原来问题可能涉及多态递归。随着 accumulate 函数的每次递归应用都会更改地图类型。

这是依赖类型的经典工作,这意味着我们根据参数值计算 return 类型。在这里我们想表达结果列表的嵌套取决于数字输入(在您的情况下,您使用列表参数的长度作为数字输入,但最好只在需要数字的地方使用数字) .

不幸的是,Haskell 还没有对依赖类型的适当支持,并且现有的变通解决方案涉及一些样板和并发症。 Idris 是一种具有类似 Haskell 语法和完全依赖类型的语言,因此我可以更清楚地说明 Idris 中的想法:

-- unary naturals from the Idris Prelude :
-- data Nat = Z | S Nat

-- compose a function n times (which can also be a type constructor!)
-- for example, iterN 3 List Int = List (List (List Int))
iterN : Nat -> (a -> a) -> a -> a
iterN Z     f a = a
iterN (S k) f a = f (iterN k f a)

mapN : (n : Nat) -> (a -> b) -> iterN n List a -> iterN n List b
mapN Z     f as = f as
mapN (S k) f as = map (mapN k f) as

-- usage:
> mapN 3 (+10) [[[0]]]
[[[10]]]
> mapN 0 id 10
10

这是完整的 Idris 解决方案。在 Haskell 中,我们不能在类型中有值或函数,实现上述工作的唯一方法是将函数的类型级版本创建为类型族,将类型的值级版本创建为单例,有效地编写两倍于理想数量的定义。 singletons 库试图通过模板 Haskell 和巧妙的机制删除大量样板文件。这是一个基于单例的解决方案:

{-# LANGUAGE DataKinds, TypeFamilies #-}

import Data.Singletons -- package: singletons
import Data.Nat        -- package: singleton-nats (by me)

type family IterN n f a where
  IterN Z     f a = a
  IterN (S n) f a = f (IterN n f a)

mapN :: Sing n -> (a -> b) -> IterN n [] a -> IterN n [] b  
mapN SZ     f a  = f a
mapN (SS n) f as = map (mapN n f) as

-- usage:
> mapN (sing :: SLit 3) (+10) [[[0]]]
[[[10]]]

好消息是,我们正在进行研究和开发以向 GHC 添加依赖类型,我们可以期待在未来几年内有所改进。


或者,人们可能会想使用类型 类 来推断 return 类型中的嵌套量。这是相当可怕的,因为我们必须区分 [a] 和非列表类型,这至少需要 OverlappingInstances,但实际上它可以接受更差的 IncoherentInstances,因为我们还想根据本地上下文解析多态类型。

{-# LANGUAGE
  UndecidableInstances, IncoherentInstances, MultiParamTypeClasses,
  FlexibleInstances, TypeFamilies #-}

class MapN a b as bs where
  mapN :: (a -> b) -> as -> bs

instance (as ~ a, bs ~ b) => MapN a b as bs where
  mapN = id

instance (MapN a b as bs, bs' ~ [bs]) => MapN a b [as] bs' where
  mapN f as = map (mapN f) as

-- usage:
> mapN (+1) 0
1
> mapN (+10) [[[0]]]
[[[10]]]

-- note though that without enough context `mapN`'s type is nonsense:
> :t mapN (+0)
mapN (+0) :: Num b => b -> b