Rob*_*gar 50 syntax layout haskell syntactic-sugar
由于其丑陋的语法和命名空间污染,许多人认为Haskell的记录语法是一种优雅语言的疣.另一方面,它通常比基于位置的替代方案更有用.
而不是像这样的声明:
data Foo = Foo {
fooID :: Int,
fooName :: String
} deriving (Show)
Run Code Online (Sandbox Code Playgroud)
在我看来,这些方面的东西会更有吸引力:
data Foo = Foo id :: Int
name :: String
deriving (Show)
Run Code Online (Sandbox Code Playgroud)
我确信必须有一个很好的理由让我失踪,但为什么采用类似C语言的记录语法采用更简洁的基于布局的方法?
其次,管道中是否有任何东西可以解决命名空间问题,所以我们可以编写id foo而不是fooID foo在Haskell的未来版本中编写?(除了目前可用的基于longwinded类型的变通办法.)
Dan*_*ton 49
好吧,如果没有其他人会尝试,那么我将采取另一个(稍微仔细研究)刺伤回答这些问题.
TL;博士
问题1:这就是掷骰子的方式.这是一个间接的选择,它被卡住了.
问题2:是(sorta).几个不同的政党当然一直在考虑这个问题.
根据我发现的相关和有趣的链接和引用,继续阅读每个答案的非常长篇解释.
为什么采用类似C语言的记录语法采用更简洁的基于布局的方法?
微软研究人员写了一篇Haskell历史论文.第5.6节谈论记录.我会引用第一个小小的内容,这是有见地的:
早期版本的Haskell最明显的遗漏之一是缺少记录,提供命名字段.鉴于记录在实践中非常有用,为什么它们被省略了?
然后,微软回答了他们自己的问题
最强烈的原因似乎是没有明显的"正确"设计.
您可以自己阅读本文的详细信息,但他们说Haskell最终采用了记录语法,因为"数据结构中命名字段的压力".
当Haskell 1.3设计正在进行时,1993年,用户对数据结构中命名字段的压力很大,因此委员会最终采用了极简设计......
你问为什么它是为什么?好吧,根据我的理解,如果早期的Haskellers有他们的方式,我们可能从来没有首先记录语法.这个想法很明显被那些已经习惯了类C语法的人推到了Haskell上,并且更感兴趣的是将类似C的东西变成Haskell,而不是做"Haskell方式".(是的,我意识到这是一个非常主观的解释.我可能是错的,但在没有更好的答案的情况下,这是我能得出的最好结论.)
管道中有什么东西可以解决命名空间问题吗?
首先,并非所有人都认为这是一个问题.几个星期前,一个球拍爱好者向我(和其他人)解释说,具有相同名称的不同功能是一个坏主意,因为它使"___这个功能有什么作用?"的分析变得复杂.事实上,它不是一个功能,而是许多功能.对于Haskell来说,这个想法可能会特别麻烦,因为它会使类型推断变得复杂.
在一点点切线上,微软有关Haskell的类型类的有趣话题:
在语言设计仍处于流行的那一刻,Wadler和Blott碰巧产生了这个关键想法,这恰好与时间巧合.
不要忘记Haskell曾经年轻.有些决定只是因为它们的制定而做出的.
无论如何,有一些有趣的方法可以解决这个"问题":
键入定向名称解析,对Haskell的建议修改(在上面的评论中提到).只需阅读该页面即可看到它涉及该语言的许多方面.总而言之,这不是一个坏主意.已经有很多想法,所以它不会与东西发生冲突.但是,它仍然需要更多的关注才能使它成为现在(更多)成熟的Haskell语言.
另一篇微软论文OO Haskell特别提出了对Haskell语言的扩展,以支持"ad hoc重载".它相当复杂,所以你只需要自己查看第4节.它的要点是自动(?)推断"Has"类型,并添加一个额外的步骤来键入检查,他们称之为"改进",在后面的选择性引用中含糊地概述:
给定类约束
Has_m (Int -> C -> r),m只有一个匹配此约束的实例...因为只有一个选择,我们现在应该做,然后修复r它Int.因此,我们得到了预期的类型f:f :: C -> Int -> IO Int... [this]只是一个设计选择,一个基于类关闭的想法Has_m
道歉不连贯的引用; 如果这对你有帮助的话,那就好了,否则就去看看报纸吧.这是一个复杂(但令人信服)的想法.
Chris Done使用Template Haskell以与OO Haskell论文(使用"Has"类型)模糊相似的方式在Haskell中提供鸭子类型.他网站上的一些互动会话样本:
?> 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*
Run Code Online (Sandbox Code Playgroud)
这需要一些样板/不寻常的语法,我个人更愿意坚持使用类型类.但感谢克里斯完成自由发表他在该地区的脚踏实地的工作.
我只是想添加一个解决命名空间问题的链接.看来GHC的重载记录字段是GHC 7.10(并且可能已经在HEAD中),使用OverloadedRecordFields扩展名.
这将允许诸如的语法
data Person = Person { id :: Int, name :: String }
data Company { name :: String, employees :: [Person] }
companyNames :: Company -> [String]
companyNames c = name c : map name (employees c)
Run Code Online (Sandbox Code Playgroud)
[编辑]这个答案只是我对此事的一些随意的想法.我推荐我的另一个答案,因为对于那个答案,我花了很多时间来查阅并引用其他人的工作.
记录语法
在黑暗中进行一些尝试:你的"基于布局"的提议语法看起来很像非记录语法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
Run Code Online (Sandbox Code Playgroud)
在后一种情况下,究竟:: 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"
Run Code Online (Sandbox Code Playgroud)
您如何知道f1 s记录更新,而不是功能应用程序?
命名空间
如果您想将类定义的id使用与Prelude的混合使用,该id怎么办?你如何指定你正在使用哪一个?你能想到比合格的导入和/或hiding关键字更好的方法吗?
import Prelude hiding (id)
data Foo = Foo {a,b,c,i,j,k :: Int, s :: String}
deriving (Show)
id = i
Run Code Online (Sandbox Code Playgroud)
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"}
Run Code Online (Sandbox Code Playgroud)
这些都不是很好的答案,但它们是我所拥有的最好的答案.我个人认为记录语法不那么难看.我确实觉得namespacing/modules的东西还有改进的余地,但我不知道如何让它变得更好.
| 归档时间: |
|
| 查看次数: |
15827 次 |
| 最近记录: |