勺子在Haskell中是不安全的吗?

所以Haskell有一个叫做spoon的库,可以让我这样做

safeHead :: [a] -> Maybe a safeHead = spoon . head 

但它也让我这样做

 >>> spoon True :: Maybe Bool Just True >>> spoon (error "fork") :: Maybe Bool Nothing >>> spoon undefined :: Maybe Bool Nothing >>> spoon (let x = x in x) :: Maybe Bool <... let's just keep waiting...> 

这在某些情况下似乎是非常有用的,但是它也违反了指称语义(就我的理解),因为它让我在的语义前像中区分不同的事物。 这比throw / catch更强大,因为它们可能具有由continuations定义的语义。

 >>> try $ return (error "thimble") :: IO (Either SomeException Bool) Right *** Exception: thimble 

所以我的问题是:有人可以使用勺子恶意破坏types安全吗? 便利是值得的危险吗? 或者,更现实的说,是否有一种合理的方式来使用它可能侵蚀某个人对程序意义的信心?

有一个棘手的地方,如果你使用它,做一个看起来像无辜的重构可以改变一个程序的行为。 没有任何花里胡哨的,这是这样的:

 fhx = hx isJust (spoon (f undefined)) --> True 

但也许这本书中最常见的haskell转换,eta收缩, f

 fh = h isJust (spoon (f undefined)) --> False 

由于seq的存在,eta收缩已经不是保持语义的; 但没有匙eta收缩只能改变一个终止程序到一个错误; 用勺子收缩可以将终止程序变成不同的终止程序。

正式的, spoon是不安全的是它在域上是非单调的 (因此可以用它来定义函数)。 而没有spoon每个function都是单调的。 所以在技术上你会失去正式推理的有用性。

以一个真实的例子来说明什么时候这将是重要的,留给读者一个练习(阅读:我认为现实生活中不太可能重要 – 除非你开始滥用它;例如使用undefined的方式Java程序员使用null

如果这就是你所得到的,你不能用spoon写出unsafeCoerce的保证。 正如它看起来一样不完善,并且恰好违背了Haskell语义。 但是,你不能用它来创build段错误或类似的东西。

但是,通过违反Haskell语义,它确实会使代码难以理解,而且,例如,带有勺子的safeHead会比直接编写的safeHead效率低。