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

时间:2015-09-12 08:35:18

标签: haskell recursion types polymorphism composition

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

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

A/B
  |---C/D
       file1.txt   
  |---D
     |---E
     |    file2.txt
     |---G
          file3.txt
          file4.txt

createComposedMaps list = accumulate list map where accumulate (x:xs) m = accumulate xs (m.map) accumulate [] m = m 无关紧要,它只是计算作品的数量。

我得到的错误是“无法构建无限类型”:

list

目标是创建一个动态组合的映射函数,我以后可以使用它。而其他半群可以附加在一起(列表,数字......)。功能似乎要困难得多。

非常感谢显示fmap和/或地图的示例。

我发现了Library function to compose a function with itself n times

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

原来问题可能涉及多态递归。因为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)’ 函数的每个递归应用都会更改地图类型。

1 个答案:

答案 0 :(得分:4)

这是依赖类型的经典工作,这意味着我们从参数值计算返回类型。在这里,我们要表示结果列表的嵌套取决于数字输入(在您的情况下,您使用列表参数的长度作为数字输入,但它可能更好地使用需要数字的数字。)

不幸的是,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库试图通过Template 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添加依赖类型,我们可以期待未来几年的改进。

或者,可能会尝试使用类型类来推断返回类型中的嵌套量。这是相当可怕的,因为我们必须区分[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