Haskell编译智能构造函数的时间检查

时间:2018-09-12 15:21:51

标签: haskell ghc

我正在学习Haskell,并通过讲座进行学习: http://www.cis.upenn.edu/~cis194/spring13/

我有:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="urn:bar-ns"
           targetNamespace="urn:bar-ns"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:foo-ns="urn:foo-ns"
           elementFormDefault="qualified">

  <xs:import namespace="urn:foo-ns" schemaLocation="foo.xsd"/>

  <xs:element name="bar" type="barType"/>

  <xs:complexType name="barType">
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:element ref="foo-ns:foo"/>
    </xs:sequence>
    <xs:attribute name="name" use="required"/>
  </xs:complexType>

</xs:schema>

这有效,但是如果我有:

module HanoiDisk(HanoiDisk, hanoiDisk) where
import Control.Exception
data HanoiDisk = HanoiDisk' Integer deriving (Show)
hanoiDisk :: Integer -> HanoiDisk 
hanoiDisk n = assert (n >= 1) $ HanoiDisk' n

我只会在运行时而不是在编译时收到错误。

我非常想了解如何完全消除运行时异常。

谁能提供替代方法?

谢谢

2 个答案:

答案 0 :(得分:3)

据我了解,当有人将函数hanoiDisk应用于小于1的参数时,您希望有一种“很好地失败”的方法。

正如评论者所说,在编译时执行此操作超出了基本Haskell的范围,因此您在日常代码中不需要它!

通过使用Either a b数据类型,您绝对可以“很好地失败”。

这个想法是,如果您有一个函数hanoiDisk :: Integer -> HanoiDisk,该函数需要一个Integer,并且如果输入为“ good”,并且应该返回一个HanoiDisk值,并且错误值为某个在输入为“不良”时进行排序,则可以使用备用构造函数对其进行编码。

Either a b数据类型的构造函数为Left aRight b,其中 错误输出将为Left a形式,而良好的输出将为Right b形式。让我们使用此代码来重写您的函数。

hanoiDisk :: Integer -> Either String HanoiDisk 
hanoiDisk n = if n >= 1 
              then Right (HanoiDisk' n)
              else Left "a hanoi disk must be least 1"

(可能)更合适的答案

让我们讨论一个简单的问题,即以编译器可接受的方式构造必须为非负数(而不是正数)的数字。

我认为问题与编译器解析数字的方式有关。每当您使用符号“ 0”,“ 1”,“ 2”,“ 3”,“ 4”,...,“ 9”来表示程序中的数字时,语言解析器都会期望最终结果符合类型,例如Int,Double等,因此当您使用这些符号时,您可能会发现某人可能在数字序列前加一个'-'并将非负数转换为负数一个。

让我们制作一个名为 Natural 的新模块,它将使我们能够创建正数。在其中,我们使用每个符号名称的前两个字母(例如tw代表“ 2”)为符号“ 0”,...,“ 1”定义“别名”。由于人类使用十进制编写自然数,因此我们创建了一个称为Natural的数据类型,该数据类型带有两个参数-我们要代表的数字的第一个数字,然后是一个后续数字的列表。最后,我们从模块中有选择地导出功能,以禁止用户“滥用”。

module Natural (ze,on,tw,th,fo,fi,si,se,ei,ni,Natural(..)) where

newtype Digit = Digit Int

ze = Digit 0
on = Digit 1
tw = Digit 2
th = Digit 3
fo = Digit 4
fi = Digit 5
si = Digit 6
se = Digit 7
ei = Digit 8
ni = Digit 9

data Natural = Nat Digit [Digit]

例如,自然数312将表示为Nat th [on,tw]

任何导入 Natural 的模块都只能访问我们导出的函数,因此尝试使用其他任何东西来定义类型Natural的值都将导致编译错误。此外,由于我们没有导出Digit构造函数,因此导入程序无法为Digit类型定义自己的值。

我遗漏了NumIntegralEqOrd等实例的定义,因为我认为它们不会增加更多的实例我的解释。

答案 1 :(得分:2)

Haskell在编译代码而不是值时检查类型。使类型依赖于值是“依赖类型”的工作。这是一个高级主题。

实现此目标的另一种方法是使hanoiDisk不能与Integer一起工作,而只能使用某些“ PositiveInteger”类型,为负数(或也为0 ..?)。这是一种更基本的方法。

没有什么要断言的-您甚至不可能写下这种类型的负值。您必须将此类型设置为NumEqOrdShow(也可能是Enum)的实例。

通常的方法是定义

data Nat = Z | S Nat 
           deriving (Eq, Show)