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,所以我们现在应该做,然后依次修改r
为Int
。 因此,我们得到了f
:f :: 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语法是丑陋的。 我确实觉得使用命名空间/模块的东西还有改进的空间,但我不知道如何使它变得更好。