Haskelllogging语法

Haskell的logging语法被许多人认为是对其他优雅语言的一种讽刺,因为它的丑陋语法和命名空间污染。 另一方面,通常比基于位置的select更有用。

而不是像这样的声明:

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

在我看来,沿着这些路线的东西会更有吸引力:

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

我肯定有一个很好的理由,我错过了,但为什么类似C语言的logging语法通过一个更干净的布局为基础的方法?

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

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

TL;博士

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

问题2:是(分类)。 当然有几个不同的党派正在考虑这个问题。

阅读一个非常长的解释每个答案,基于链接和报价,我发现是相关和有趣的。

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

微软研究人员撰写了“哈斯格尔历史”论文。 第5.6节讨论logging。 我会引用第一个小点,这是非常有见地的:

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

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

最强烈的原因似乎是没有明显的“正确”的devise。

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

到Haskell 1.3devise开始的时候,在1993年,用户对数据结构中命名域的压力很大,因此委员会最终采用了简约devise。

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

有没有什么可以解决名称空间问题?

首先,不是每个人都觉得这是一个问题。 几个星期前,一个球拍爱好者向我(和其他人)解释说,具有不同的同名function是一个坏主意,因为这使得分析“命名为___的函数做了什么? 事实上,这不是一个function,而是许多function。 这个想法对Haskell来说可能会很麻烦,因为它会使types推断变得复杂。

在微软的切线上,微软对Haskell的types特性有一些有趣的说法:

Wadler和Blott在语言devise还处于stream动的时刻碰巧出现了这个关键的想法,这是时间的巧合。

不要忘记,Haskell曾经很年轻。 有些决定只是因为它们的制定而已。

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

键入定向名称parsing ,对Haskell的修改build议(在上面的注释中提到)。 只要阅读该页面,就可以看到它触及了该语言的很多领域。 总而言之,这不是一个坏主意。 已经有了很多的想法,所以它不会与东西冲突。 然而,它仍然需要更多的关注,才能把它带入现在(更成熟)的Haskell语言。

另一篇微软论文, OO Haskell特别提出了对Haskell语言的扩展,以支持“ad hoc overloading”。 这是相当复杂的,所以你只需要自己检查第4部分。 它的要点是自动(?)推断“有”types,并添加一个额外的步骤来键入检查,他们称之为“改善”,在下面的select性引用中模糊地概述:

给定类的约束Has_m (Int -> C -> r)只有一个m的实例匹配这个约束…因为只有一个select,所以我们现在应该做,然后依次修改rInt 。 因此,我们得到了ff :: C -> Int -> IO Int的期望types… [这个]只是一个deviseselect,一个基于Has_m类closures的思想

对不连贯的引用抱歉; 如果那对你有帮助,那么很好,否则就去看报纸吧。 这是一个复杂的(但令人信服的)想法。

Chris Done使用模板Haskell 在Haskell中以与OO Haskell文件(使用“Has”types)模糊的方式提供鸭子打字 。 他的网站上的一些互动会话样本:

 λ> 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* 

这需要一些样板/不寻常的语法,我个人更喜欢坚持types类。 但是对Chris Done自由地发表他在这个地区的踏实的工作表示赞赏。

我只是想我会添加一个链接来解决命名空间问题。 看起来, GHC的重载logging字段在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) 

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

logging语法

在黑暗中进行一些刺探:“基于布局的”build议语法看起来很像非logging语法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应用于? 整个数据声明?

logging语法(当前)的声明与构造和更新语法相似。 基于布局的语法在这些情况下不会更清晰; 你怎么parsing这些额外的=标志?

 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是一个logging更新,而不是一个函数应用程序?

命名空间

如果你想混合使用你的类定义的id和Prelude的id呢? 你如何指定你正在使用哪一个? 你能想到比合格的import和/或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"} 

这些不是很好的答案,但他们是我所拥有的最好的。 我个人不认为logging语法是丑陋的。 我确实觉得使用命名空间/模块的东西还有改进的空间,但我不知道如何使它变得更好。