我正在学习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
我只会在运行时而不是在编译时收到错误。
我非常想了解如何完全消除运行时异常。
谁能提供替代方法?
谢谢
答案 0 :(得分:3)
据我了解,当有人将函数hanoiDisk
应用于小于1的参数时,您希望有一种“很好地失败”的方法。
正如评论者所说,在编译时执行此操作超出了基本Haskell的范围,因此您在日常代码中不需要它!
通过使用Either a b
数据类型,您绝对可以“很好地失败”。
这个想法是,如果您有一个函数hanoiDisk :: Integer -> HanoiDisk
,该函数需要一个Integer
,并且如果输入为“ good”,并且应该返回一个HanoiDisk
值,并且错误值为某个在输入为“不良”时进行排序,则可以使用备用构造函数对其进行编码。
Either a b
数据类型的构造函数为Left a
和Right 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
类型定义自己的值。
我遗漏了Num
,Integral
,Eq
,Ord
等实例的定义,因为我认为它们不会增加更多的实例我的解释。
答案 1 :(得分:2)
Haskell在编译代码而不是值时检查类型。使类型依赖于值是“依赖类型”的工作。这是一个高级主题。
实现此目标的另一种方法是使hanoiDisk
不能与Integer
一起工作,而只能使用某些“ PositiveInteger
”类型,为负数(或也为0 ..?)。这是一种更基本的方法。
没有什么要断言的-您甚至不可能写下这种类型的负值。您必须将此类型设置为Num
,Eq
,Ord
和Show
(也可能是Enum
)的实例。
通常的方法是定义
data Nat = Z | S Nat
deriving (Eq, Show)