2011-03-20 135 views
41

许多人认为Haskell的记录语法是对其他语言优雅的疣,因为它的丑陋语法和命名空间污染。另一方面,它通常比基于位置的替代方案更有用。Haskell记录语法

而是像这样的声明:

data Foo = Foo { 
    fooID :: Int, 
    fooName :: String 
} deriving (Show) 

这在我看来,这些方针的东西会比较有吸引力:

data Foo = Foo id :: Int 
       name :: String 
       deriving (Show) 

我敢肯定,必须有一个很好的理由我错过了,但为什么采用基于布局的更清晰的C语言语法?

其次,有没有什么可以解决命名空间问题,所以我们可以在将来的Haskell版本中编写id foo而不是fooID foo? (除了目前可用的基于longwinded类的解决方法之外)。

+14

的语法是好的 - 什么是“疣”存在的事实,记录是不可扩展的(人们希望更多的权力)和名字空间的问题更在于。正如我习惯了常规语法,其中一种情况是我更喜欢它布局语法 - 也许没有人考虑过Haskell设计时的布局语法? “基于类型的名称解析”TBNR已被提出用于第二个问题 - 许多人似乎认为它存在语义上的问题(这不利于该提案几乎没有说服力的书写),但GHC-HQ的一名成员似乎喜欢它,所以它可能会出现。 – 2011-03-20 08:41:41

+2

@Stephen Tetley:你为什么不把它作为答案?这很棒! – fuz 2011-03-20 09:38:32

+2

在我上面的评论中,我的意思是TDNR - 'Type Directed Name Resolution'不幸在这种情况下Stack Overflow不允许我编辑评论。 @FUZxxl - 谢谢,但我认为这是一个意见,因此它似乎是一个评论,而不是一个答案,它肯定不是普遍的观点。 – 2011-03-20 11:24:49

回答

42

那么如果没有人会尝试,那么我会采取另一个(稍微更仔细研究)刺回答这些问题。

TL;博士

问题1:这只是骰子滚的方式。这是一个环境选择,它卡住了。

问题2:是(sorta)。当然有几个不同的方面正在考虑这个问题。

请仔细阅读,以获得对每个答案的非常长期的解释,并基于我发现的相关和有趣的链接和引语。

为什么采用基于布局的更清晰的C语言语法?

微软研究人员写了一个History of Haskell论文。第5.6节讨论记录。我还要举第点点,这是有见地:

一位来自哈斯克尔 的早期版本最明显的疏漏是没有记录的,提供名为网络的视场。鉴于 记录在实践中非常有用,它们为什么被省略?

的Microsofties然后回答自己的问题

最强的理由似乎是,没有明显的“恰到好处”的设计。

你可以自己阅读这篇文章的细节,但他们说Haskell最终采用了记录语法,因为“数据结构中命名字段的压力”。

由当时的哈斯克尔1.3设计正在进行中,在1993年,用户 压力在数据结构命名的网络连接的视场为强,因此该委员会最终采用了简约的设计......

你问为什么这是为什么?那么,据我所知,如果早期的Haskellers有他们的方式,我们可能从来没有记录语法。这个想法显然已经被那些已经习惯了类C语法的人推到了Haskell上,并且更有兴趣将类似C的东西变成Haskell,而不是“Haskell的方式”。 (是的,我知道这是一个非常主观的解释。我可能是完全错误的,但在没有更好的答案,这是我可以得出的最佳结论。)

是否有管道什么解决命名空间问题?

首先,不是每个人都觉得这是一个问题。几个星期前,一位发烧友向我(以及其他人)解释说,具有不同名称的函数是一个坏主意,因为它会使“名为___的函数做什么”的分析变得复杂。事实上,这不是一种功能,而是许多功能。这个想法对于Haskell来说可能会很麻烦,因为它会使类型推断变得复杂。

有轻微的切线,该Microsofties有有趣的事情谈谈Haskell的类型类:

这是时间的一个令人高兴的巧合 是Wadler和Blott发生在刚刚的那一刻时,产生这种核心思想 语言设计仍在流行。

别忘了哈斯克尔还年轻一次。有些决定仅仅是因为它们的制定而已。

不管怎么说,有这个“问题”可以处理一些有趣的方法:

Type Directed Name Resolution,提议修改哈斯克尔(在评论上述)。只要阅读该页面即可看到它涉及该语言的很多领域。总而言之,这不是一个坏主意。已经考虑了很多想法,以便它不会与东西发生冲突。但是,它仍然需要更多的关注才能将其融入现在(更成熟)的Haskell语言中。

另一篇Microsoft文件OO Haskell特别提出了对Haskell语言的扩展以支持“ad hoc过载”。这很复杂,所以你只需要自己检查第4部分。它的要点是自动(?)推断“有”类型,并添加一个额外的步骤,类型检查他们称之为“改进”,在下面的选择性引用中模糊地概述:

鉴于类约束Has_m (Int -> C -> r)有 只有一个m匹配这个约束......因为只有一个选择,我们现在应该做出来,并且 将r修复为Int。因此,我们得到预期的类型ff :: C -> Int -> IO Int ... [这]是一个简单的 设计选择,以及一个基于这样的思想:Has_m关闭

道歉用于松散报价;如果这对你有所帮助,那么很好,否则就去阅读论文。这是一个复杂的(但令人信服的)想法。

Chris Done使用Template Haskell以与OO Haskell纸张类似的方式提供duck typing in Haskell(使用“Has”类型)。从他的网站有几个交互式会话样本:

λ> flap ^. donald 
*Flap flap flap* 
λ> flap ^. chris 
I'm flapping my arms! 

fly :: (Has Flap duck) => duck -> IO() 
fly duck = do go; go; go where go = flap ^. duck 

λ> fly donald 
*Flap flap flap* 
*Flap flap flap* 
*Flap flap flap* 

这需要一点点样板/不寻常的语法,我个人宁愿坚守类型类。但是对克里斯·多恩自由地发表他在该地区的踏实工作表示赞赏。

+4

为哈斯克尔历史链接+1 :) – 2011-04-02 03:25:35

+23

“微软研究人员撰写哈斯格尔历史论文”听起来有偏见。该论文由Haskell的四名设计师和创作者撰写;而今天恰好如此,他们中的一人Simon Peyton Jones在微软研究院工作(并在MSR网站上主持他的论文)。所以你认为Haskell设计背后的人是“微软” - 我认为这是一个有点贬义的词。 – 2013-08-26 11:26:04

+0

@Joker_vD你知道你可以建议一个编辑权吗?如果这是真的,我想看看它编辑。 – corazza 2016-01-02 16:09:02

5

这个答案只是我在这个问题上的一些随机想法。我推荐我的另一个答案,因为为了这个答案,我花了很多时间去查阅和参考其他人的工作。

记录语法

以在黑暗中的几个刺:你的“布局为基础”的提出语法看起来很像非记录语法data声明;这可能会引起混淆解析(?)

--record 
data Foo = Foo {i :: Int, s :: String} deriving (Show) 
--non-record 
data Foo = Foo Int String deriving (Show) 
--new-record 
data Foo = Foo i :: Int, s :: String deriving (Show) 

--record 
data LotsaInts = LI {a,b,c,i,j,k :: Int} 
--new-record 
data LostaInts = LI a,b,c,i,j,k :: Int 

在后一种情况下,究竟是什么:: Int应用?整个数据声明?

带有记录语法(当前)的声明与构造和更新语法类似。对于这些情况,基于布局的语法而不是更清晰;你如何解析那些额外的=标志?

let f1 = Foo {s = "foo1", i = 1} 
let f2 = f1 {s = "foo2"} 

let f1 = Foo s = "foo1", i = "foo2" 
let f2 = f1 s = "foo2" 

你怎么知道f1 s是一个记录更新,而不是一个函数应用程序?

命名空间

如果你想交融的使用你的类定义与前奏的idid?你如何指定你正在使用哪一个?你能想到比合格进口和/或hiding关键字更好的方法吗?

import Prelude hiding (id) 

data Foo = Foo {a,b,c,i,j,k :: Int, s :: String} 
       deriving (Show) 

id = i 

ghci> :l data.hs 
ghci> let foo = Foo 1 2 3 4 5 6 "foo" 
ghci> id foo 
4 
ghci> Prelude.id f1 
Foo {a = 1, b = 2, c = 3, i = 4, j = 5, k = 6, s = "foo"} 

这些都不是伟大的答案,但他们是我已经得到了最好。我个人不认为记录语法是丑。我确实觉得使用命名空间/模块的东西还有改进的空间,但我不知道如何使它变得更好。

+0

感谢您的回复,但FWIW我的布局版本只是作为一个示例,而不是一个应该这样做的建议。我同意命名空间问题更重要 - 希望像@ stephen的TDNR这样的东西能够获得一些吸引力。 – 2011-03-21 23:26:15

+0

可能是自动的/必要的限定条件,比如'data Foo = Foo {id :: Int}'和'Foo.id'?它使宣言简洁明了,用法清晰。 – 2014-07-26 01:05:20

8

我只是想我会添加一个链接来解决命名空间问题。看来overloaded record fields for GHC即将在GHC 7.10(并且可能已经在HEAD中)使用OverloadedRecordFields extension

这将使语法如

data Person = Person { id :: Int, name :: String } 
data Company { name :: String, employees :: [Person] } 

companyNames :: Company -> [String] 
companyNames c = name c : map name (employees c) 
+0

只是一个附录 - OverloadedRecordFields避免了Dan Burton的回答上面指出的关于类型推断的问题,只允许它们用于已经可以清楚地推断出类型的情况。在实践中,这是大多数情况下(至少如果您遵循声明顶级函数类型的常见习惯用法)。现在有扩展正在工作,扩展了它们可以用于类型推断或多态的各种方式,包括一个由给定字段名称的所有记录实现的提议的自动实例化类型类。 – Jules 2017-07-15 15:10:08

+0

...这与Dan Burton的答案中引用的Chris Done的鸭式输入模板Haskell有点类似,除了没有任何需要使用Template Haskell和更直接的语法。 – Jules 2017-07-15 15:18:44