简体   繁体   English

在 haskell 中使用替代前奏

[英]Using alternative preludes in haskell

I'm interested in alternative Preludes.我对替代 Preludes 感兴趣。 I understand there are many choices:我知道有很多选择:

  1. https://hackage.haskell.org/packages/#cat:Prelude https://hackage.haskell.org/packages/#cat:Prelude
  2. https://guide.aelve.com/haskell/alternative-preludes-zr69k1hc https://guide.aelve.com/haskell/alternative-preludes-zr69k1hc

I understand one simple thing a lot of them fix is text, and another is in functions like head that error pretty hard when you might prefer they are safer.我知道很多人修复的一个简单的事情是文本,另一个是像head这样的函数,当你可能更喜欢它们更安全时,这个错误很难。

However, when I try to use these alternatives, the behavior in head , hmm, just seems to break the function completely, and doesn't look like an improvement to me.但是,当我尝试使用这些替代方法时, head的行为,嗯,似乎完全破坏了该功能,对我来说似乎没有改进。 Here are some examples:这里有些例子:

Prelude序幕

Prelude> head [1]
1
Prelude> head []
*** Exception: Prelude.head: empty list

Foundation基础

Foundation> head [1]

<interactive>:6:6: error:
    • Couldn't match expected type ‘NonEmpty c’
                  with actual type ‘[Integer]’
    • In the first argument of ‘head’, namely ‘[1]’
      In the expression: head [1]
      In an equation for ‘it’: it = head [1]
    • Relevant bindings include
        it :: foundation-0.0.21:Foundation.Collection.Element.Element c
          (bound at <interactive>:6:1)
Foundation> head []

<interactive>:7:6: error:
    • Couldn't match expected type ‘NonEmpty c’ with actual type ‘[a0]’
    • In the first argument of ‘head’, namely ‘[]’
      In the expression: head []
      In an equation for ‘it’: it = head []
    • Relevant bindings include
        it :: foundation-0.0.21:Foundation.Collection.Element.Element c
          (bound at <interactive>:7:1)

Safe安全的

Safe> head []

<interactive>:22:1: error: Variable not in scope: head :: [a0] -> t

Classy Prelude优雅的前奏

ClassyPrelude> head [1]

<interactive>:24:6: error:
    • Couldn't match expected type ‘NonNull mono’
                  with actual type ‘[Integer]’
    • In the first argument of ‘head’, namely ‘[1]’
      In the expression: head [1]
      In an equation for ‘it’: it = head [1]
    • Relevant bindings include
        it :: Element mono (bound at <interactive>:24:1)

Relude重装

Relude> head [1]

<interactive>:27:6: error:
    • Couldn't match expected type ‘NonEmpty a’
                  with actual type ‘[Integer]’
    • In the first argument of ‘head’, namely ‘[1]’
      In the expression: head [1]
      In an equation for ‘it’: it = head [1]
    • Relevant bindings include it :: a (bound at <interactive>:27:1)

Rio里约

RIO> head [1]

<interactive>:7:1: error:
    Variable not in scope: head :: [Integer] -> t

Protolude简介

Protolude> head [1]
Just 1
Protolude> head []
Nothing

This looks good---it also works for tail, right?这看起来不错——它也适用于尾巴,对吧?

Protolude> tail [1]

<interactive>:12:1: error:
    • Variable not in scope: tail :: [Integer] -> t
    • Perhaps you meant ‘tails’ (imported from Protolude)

Protolude> tails [1]
[[1],[]]

Protolude> tails []
[[]]

Well, that's not exactly a drop-in replacement.嗯,这不完全是一种替代品。

What am I missing in why this is better, why these functions have been defined if they're just going to fail?我错过了什么为什么这更好,为什么这些功能会被定义,如果它们会失败?

In most cases, they are being introduced because they fail at compile time instead of runtime.在大多数情况下,它们被引入是因为它们在编译时而不是运行时失败。

The problem with Prelude.head is not (only) that it can fail. Prelude.head的问题不仅仅是(仅)它可能会失败。 It is that it has to, since there is no way to take a list [a] and always produce an element a , since the input list might be empty.这是它必须的,因为没有办法获取一个列表[a]并总是产生一个元素a ,因为输入列表可能是空的。 There is no easy fix that is a drop-in replacement, a radical change is needed.没有简单的修复方法是直接替换,需要彻底的改变。

A safer, and arguably better prelude can address this issue in one of the following ways:一个更安全、可以说更好的前奏可以通过以下方式之一解决这个问题:

  • remove head , so that the programmer won't use a dangerous tool.删除head ,这样程序员就不会使用危险的工具。 Any use of head will fail, at compile time.在编译时,任何使用head都会失败。 Not great, but OK.不是很好,但还可以。

  • restrict the input type, eg head :: NonEmptyList a -> a .限制输入类型,例如head :: NonEmptyList a -> a This will be usable, but the programmer has to adapt the code so to guarantee that the input list is really non empty.这将是可用的,但程序员必须修改代码以保证输入列表真的非空。 Just passing a nonempty list won't do for the compiler -- the compiler wants a proof , and rightly so.仅仅传递一个非空列表对编译器不起作用——编译器需要一个proof ,这是正确的。 The good news is that the previous code will be littered with compile errors, which will help the programmer spot the parts of the program which need to be fixed.消息是,之前的代码将充满编译错误,这将有助于程序员发现程序中需要修复的部分。

  • restrict the output type, eg head :: [a] -> Maybe a .限制输出类型,例如head :: [a] -> Maybe a This can be used just fine, but the programmer will need to cope with the different result type, and handle all the potential Nothing s.这可以很好地使用,但程序员需要处理不同的结果类型,并处理所有潜在的Nothing s。 Again, the compile time errors will help the programmer to identify where some fixes are needed.同样,编译时错误将帮助程序员确定需要修复的地方。

In any case, the programmer has to modify the code.在任何情况下,程序员都必须修改代码。 There's no way around it.没有办法解决。 However, once the compile time errors are resolved, the program is guaranteed to never produce head: empty list errors at runtime.然而,一旦编译时错误得到解决,程序就保证永远不会在运行时产生head: empty list错误。

I'm one of the relude authors and I can provide a bit motivation regarding why relude choose this behaviour for head , tail , last and init functions.我是relude作者之一,我可以提供一些关于为什么reludeheadtaillastinit函数选择这种行为的动机。

Standard Prelude defines head in the following way:标准Prelude以下列方式定义head

head :: [a] -> a

Alternative preludes often define head as follows:替代前奏曲通常将head定义如下:

head :: [a] -> Maybe a

However, relude implements it with the following type signature instead:但是, relude使用以下类型签名来实现它:

head :: NonEmpty a -> a

This design decision makes the library less beginner-friendly (people might not expect such type of the head function) but on the other hand it makes the interface more type-safe.这种设计决策使库不太适合初学者(人们可能不期望这种类型的 head 函数),但另一方面它使界面更加类型安全。

Another reason for this: if you have function of type head :: [a] -> Maybe a , you can't express head :: NonEmpty a -> a using your Maybeised version of head .另一个原因是:如果你有head :: [a] -> Maybe a类型的函数,你不能用你的Maybeised版本的head表达head :: NonEmpty a -> a But if you have head that works with NonEmpty it's quite easy to implement head that returns Maybe a .但是,如果您的headNonEmpty则很容易实现返回Maybe a head And relude even has the viaNonEmpty function for that:并且relude甚至viaNonEmpty提供了viaNonEmpty函数:

viaNonEmpty :: (NonEmpty a -> b) -> ([a] -> Maybe b)

See the docs with examples here:在此处查看带有示例的文档:

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

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