Layout Haskell记录语法

Layout Haskell记录语法,layout,haskell,syntax,syntactic-sugar,Layout,Haskell,Syntax,Syntactic Sugar,Haskell的记录语法被许多人认为是一种优雅语言的缺点,因为它丑陋的语法和名称空间污染。另一方面,它通常比基于位置的备选方案更有用 而不是像这样的声明: data Foo = Foo { fooID :: Int, fooName :: String } deriving (Show) 在我看来,沿着这些路线的东西会更有吸引力: data Foo = Foo id :: Int name :: String der

Haskell的记录语法被许多人认为是一种优雅语言的缺点,因为它丑陋的语法和名称空间污染。另一方面,它通常比基于位置的备选方案更有用

而不是像这样的声明:

data Foo = Foo { 
  fooID :: Int, 
  fooName :: String 
} deriving (Show)
在我看来,沿着这些路线的东西会更有吸引力:

data Foo = Foo id   :: Int
               name :: String
               deriving (Show)
我肯定我遗漏了一个很好的理由,但是为什么在基于更干净布局的方法中采用了类似C的记录语法呢

第二,是否有解决名称空间问题的方法,这样我们就可以在Haskell的未来版本中编写
IDfoo
而不是
fooidfoo
?(除了目前可用的冗长的基于类型的解决方法之外。)

[edit]这个答案只是我对这个问题的一些随机想法。我推荐我的另一个答案而不是这个,因为对于这个答案,我花了更多的时间去查阅和参考别人的作品

记录语法

暗中试探一下:您提出的“基于布局”的语法看起来很像非记录语法
数据
声明;这可能会导致解析混淆(?)

在后一种情况下,
::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是记录更新,而不是函数应用程序

名称空间

如果您想将您定义的类的用法
id
与前奏曲的
id
混合使用,该怎么办?您如何指定使用哪一个?你能想出比限定的导入和/或
隐藏
关键字更好的方法吗

import Prelude hiding (id)

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

id = i



这些不是很好的答案,但它们是我得到的最好的答案。我个人认为记录语法没有那么难看。我确实觉得名称空间/模块的东西还有改进的余地,但我不知道如何做得更好。

如果没有其他人尝试,那么我将再次尝试(稍微仔细研究一下)回答这些问题

tl;dr

问题1:这就是掷骰子的方式。这是一个偶然的选择,它坚持了下来

问题2:是(排序)。几个不同的党派肯定一直在考虑这个问题

继续阅读,根据我发现的相关和有趣的链接和引用,对每个答案进行冗长的解释

为什么采用C类记录语法而不是更简洁的基于布局的方法?


微软的研究人员写了一篇论文。第5.6节讨论了记录。我将引用第一点,这是很有见地的:

Haskell早期版本中最明显的遗漏之一 是因为没有记录,提供了命名字段。鉴于此 记录在实践中非常有用,为什么会被省略

微软然后回答他们自己的问题

最主要的原因似乎是没有明显的“正确”设计

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

1993年Haskell 1.3设计开始时,用户 数据结构中指定领域的压力很大,因此委员会最终采用了极简设计

你问为什么是为什么?好吧,据我所知,如果早期的Haskeller有他们的方式,我们可能从一开始就没有记录语法。这个想法显然是由那些已经习惯了类C语法的人推到Haskell身上的,他们更感兴趣的是将类C的东西引入Haskell,而不是“用Haskell的方式”做事。(是的,我意识到这是一个非常主观的解释。我可能大错特错,但在没有更好答案的情况下,这是我能得出的最好结论。)

管道中是否有解决名称空间问题的方法?

首先,并非所有人都认为这是一个问题。几周前,一位热心人士向我(和其他人)解释说,使用相同名称的不同函数是个坏主意,因为这会使“名为___;的函数做什么?”的分析复杂化。事实上,这不是一个函数,而是多个函数。这个想法对Haskell来说可能是额外的麻烦,因为它使类型推断复杂化了

有一点相切,微软对Haskell的类型类有一些有趣的说法:

这是一个令人高兴的巧合 Wadler和Blott产生这个关键想法的时机 就在语言设计仍在流行的时候

别忘了哈斯克尔曾经很年轻。有些决定仅仅是因为它们被做出了

无论如何,有几个有趣的方法可以解决这个“问题”:

,建议对Haskell进行修改(在上述评论中提及)。只要看看那一页,就会发现它涉及到语言的许多领域。总而言之,这不是个坏主意。为了不与其他东西发生冲突,我们在这本书中花了很多心思。然而,它仍然需要更多的关注,以使它成为现在(更)成熟的Haskell语言

微软的另一篇论文专门提出了对Haskell语言的扩展,以支持“临时重载”。这相当复杂,所以你只需要亲自查看第4节。其要点是自动(?)推断“Has”类型,并在类型检查中添加一个额外的步骤,称之为“改进”,在下面的选择性引号中模糊地概述:

给定类约束
有m(Int->C->r)
有 m只有一个实例
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"}
λ> 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*
data Person = Person { id :: Int, name :: String }
data Company { name :: String, employees :: [Person] }

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