简体   繁体   English

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

[英]Haskell Polymorphic Recursion with Composed Maps causes Infinite Type Error

What is the right way to create a function that can dynamically create composed map? 创建可以动态创建合成地图的功能的正确方法是什么?

This results in an error (also happens with fmap): 这会导致错误(fmap也会发生):

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

The list is irrelevant, it's just there to count the number of compositions. 这个list是无关紧要的,它只是计算组合的数量。

The error I get back is about "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)’

The goal is to create a dynamically composed mapping function that I can later use. 目标是创建一个动态组合的映射函数,我以后可以使用它。 While other semigroups can be appended together (lists, numbers...). 而其他半群可以附加在一起(列表,数字......)。 Functions seems to be much harder. 功能似乎要困难得多。

Would appreciate an example showing fmap and/or map. 非常感谢显示fmap和/或地图的示例。

I found this Library function to compose a function with itself n times 我发现这个库函数可以自己组合一个函数n次

But I need to use each intermediate composition, not just the final composition. 但我需要使用每种中间成分,而不仅仅是最终成分。 And some examples still gives me infinite type error. 一些例子仍然给我无限的类型错误。

Turns out the problem may involve polymorphic recursion. 原来问题可能涉及多态递归。 As each recursive application of the accumulate function changes the map type. 因为accumulate函数的每个递归应用都会更改地图类型。

This is a classic job for dependent types, which means that we compute return types from the values of arguments. 这是依赖类型的经典工作,这意味着我们从参数值计算返回类型。 Here we'd like to express that the nesting of the resulting list depends on a numeric input (in your case, you used the length of a list parameter as the numeric input, but it's probably better to just use numbers where numbers are needed). 这里我们要表示结果列表的嵌套取决于数字输入(在您的情况下,您使用列表参数的长度作为数字输入,但最好只使用需要数字的数字) 。

Unfortunately Haskell doesn't yet have proper support for dependent typing, and existing workaround solutions involve some boilerplate and complications. 不幸的是,Haskell还没有对依赖类型的适当支持,现有的解决方案解决方案涉及一些样板和复杂性。 Idris is a language with Haskell-like syntax and full dependent types, so I can illustrate the idea in Idris with greater clarity: 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

This is the complete Idris solution. 这是完整的Idris解决方案。 In Haskell, we can't have values or functions in types, and the only way to make the above work is to create type-level versions of functions as type families and value-level versions of types as singletons, effectively writing twice as many definitions as would be ideal. 在Haskell中,我们不能在类型中使用值或函数,并且完成上述工作的唯一方法是创建函数的类型级版本作为类型族和类型的值级版本作为单例,有效地写入两倍定义是理想的。 The singletons library seeks to remove the bulk of the boilerplate through Template Haskell and clever machinery. singletons库试图通过模板Haskell和巧妙的机器去除大量的样板。 Here's a singletons-based solution: 这是一个基于单一的解决方案:

{-# 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]]]

The good news though is that there's ongoing research and development to add dependent types to GHC and we can expect improvements in the next few years. 好消息是,正在进行研究和开发,以便为GHC添加依赖类型,我们可以预期在未来几年内会有所改善。


Alternatively, one might be tempted to use type classes to infer the amount of nestedness in the return type. 或者,可能会尝试使用类型类来推断返回类型中的嵌套量。 This is fairly horrible, because we have to distinguish [a] and non-list types, which at the very least requires OverlappingInstances , but in practice it works acceptably with the even worse IncoherentInstances , because we'd also like to resolve polymorphic types depending on the local context. 这是相当可怕的,因为我们必须区分[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