为什么GHC不能推理某些无限列表呢?

时间:2017-02-23 16:24:41

标签: list haskell ghc compiler-optimization

This recent question让我想到了Haskell使用无限列表的能力。关于StackOverflow上的无限列表有plenty of other个问题和答案,我理解为什么我们不能为所有无限列表提供通用解决方案,但为什么可以&#39 ; Haskell关于一些无限列表的原因?

让我们使用第一个链接问题中的示例:

list1 = [1..]
list2 = [x | x <- list1, x <= 4]
print list2
$ [1,2,3,4

@ user2297560在评论中写道:

  

假装你是GHCI。您的用户会为您提供无限列表,并要求您查找该列表中小于或等于4的所有值。您将如何进行此操作? (请记住,您不知道列表是否有序。)

在这种情况下,用户没有为您提供无限列表。 GHC产生了它!实际上,它是按照自己的规则生成的。 Haskell 2010 Standard声明如下:

enumFrom       :: a -> [a]            -- [n..]  
  

对于Int和Integer类型,枚举函数具有以下含义:

     
      
  • 序列enumFrom e1是列表[e1e1 + 1,e1 + 2,...]。
  •   

在回答另一个问题时,@ chepner写道:

  

你知道列表单调增加,但Haskell没有。

这些用户所做的陈述似乎并不符合我的标准。 Haskell使用单调增加以有序的方式创建了列表。 Haskell 应该知道列表是有序且单调的。那么为什么这个无限列表能够自动将[x | x <- list1, x <= 4]转换为takeWhile (<= 4) list1呢?

2 个答案:

答案 0 :(得分:9)

理论上,可以想象一个重写规则,例如

{-# RULES
  "filterEnumFrom" forall (n :: Int) (m :: Int).
                     filter (< n) (enumFrom m) = [m..(n-1)]
  #-}

这会自动将filter (< 4) (enumFrom 1)等表达式转换为[1..3]。所以可能。但是有一个明显的问题:这个完全语法模式的任何变化都无法奏效。结果是你最终定义了规则,你可以更长时间地确定它们是否在触发。如果您不能依赖规则,那么您最终不会使用它们。 (另外,请注意我已经将规则专门用于Int - 正如作为评论简要发布的那样,这可能会以其他类型的细微方式分解。)

在一天结束时,要进行更高级的分析,GHC必须在列表中附加一些跟踪信息,以说明它们是如何生成的。这可能会使列表不那么轻量级的抽象,或者意味着GHC会在其中有一些特殊的机制只是来在编译时优化列表。这些选项都不是很好。

也就是说,您始终可以通过在列表的顶部上创建列表类型来添加自己的跟踪信息。

data List a where
  EnumFromTo :: Enum a => a -> Maybe a -> List a
  Filter :: (a -> Bool) -> List a -> List a 
  Unstructured :: [a] -> List a

可能最终更容易优化。

答案 1 :(得分:6)

  

那么为什么不能推断这个无限列表会自动将View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { changeColorBack(textView1); changeColorBack(textView2); changeColorBack(textView3); changeColorBack(textView4); switch (v.getId()) { case R.id.textView1: changeColor(textView1); break; case R.id.textView2: changeColor(textView2); break; case R.id.textView3: changeColor(textView3); break; case R.id.textView4: changeColor(textView4); } } }; textView1.setOnClickListener(listener); textView2.setOnClickListener(listener); textView3.setOnClickListener(listener); textView4.setOnClickListener(listener); 转换为with c as ( select left(@CustomerId, charindex(',', @CustomerId) - 1) as customerid, substring(@CustomerId, charindex(',', @CustomerId) + 1, 8000) as rest union all select left(rest, charindex(',', rest) - 1), substring(rest, charindex(',', rest) + 1, 8000) from c where rest like '%,%' ) insert into t(customerid, Address, City) select customerid, @Address, @City from c where customerid <> '';

答案并不比“它不使用[x | x <- list1, x <= 4]更具体,因为它不使用takeWhile (<= 4) list1”。 The spec says

  

翻译:列表推导满足这些身份,可能   用作内核的翻译:

takeWhile

也就是说,列表理解的含义是通过翻译成一个更简单的语言takeWhile - 表达式,[ e | True ] = [ e ] [ e | q ] = [ e | q, True ] [ e | b, Q ] = if b then [ e | Q ] else [] [ e | p <- l, Q ] = let ok p = [ e | Q ] ok _ = [] in concatMap ok l [ e | let decls, Q ] = let decls in [ e | Q ] - 绑定和调用if来给出的。我们可以通过以下步骤翻译它来弄清楚你的例子的含义:

let