繁体   English   中英

具有组合映射的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是无关紧要的,它只是计算组合的数量。

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

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和/或地图的示例。

我发现这个库函数可以自己组合一个函数n次

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

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

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

不幸的是,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添加依赖类型,我们可以预期在未来几年内会有所改善。


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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM