美丽而可怕的Haskell

Ada*_*der 3 haskell functional-programming

哈斯克尔很漂亮.这是事实.它简洁,快速等等.你们许多人都会承认写作是一件非常愉快的事情.另一方面,我认为当人们尝试编写过于复杂的代码时,它的简洁性可能是一个缺点.例如,我最近打开了我朋友的项目,发现了这样的事情:

stationDepartures id sc=map (\(s1,list)->(stationName $ (stationMap $ system sc) Map.! s1,list)) ( Map.toList (Map.fromListWith (++) (map (\((s1,s2),(d,start,dur))->(s2,[start])) (Map.toList (Map.filterWithKey (\(s1,s2) _ ->s1==id) (Map.unions (List.map times (Map.elems $ schedule sc))))))))
Run Code Online (Sandbox Code Playgroud)

这个单线对我来说绝对不可读.这当然是一个非常极端的例子,但它帮助我意识到可能我的Haskell代码似乎对其他人来说是不可读的.我开始想知道在Haskell中创建漂亮代码的原则是什么.我发现例如lambda函数被某些人认为是多余的,因为它们使代码的可读性降低.你觉得怎么样?Haskell代码应满足哪些要求才能被视为"美丽"?

Die*_*Epp 18

我会假设你问,"我怎么能以更易读的方式写这个单行?" 否则,问题有点过于主观.

单行在任何语言中都存在问题,尽管由于二进制运算符(具有优先级规则),高阶函数(难以命名)和(喘气!)高阶函数的丰富,Haskell可能特别成问题.二元运算符.但是,我们可以首先注意到大部分括号可以在单行中消除,我们可以证明表达式是一系列组合在一起的函数.每个函数都可以写在一个单独的行上,并由.一个final 组成,$将整个事物应用于一个参数.

stationDepartures id sc =
  map (\(s1,list) -> (stationName $ (stationMap $ system sc) Map.! s1,
                      list)) .
  Map.toList .
  Map.fromListWith (++) .
  map (\((s1,s2),(d,start,dur)) -> (s2,[start])) .
  Map.toList .
  Map.filterWithKey (\(s1,s2) _ -> s1 == id) .
  Map.unions .
  List.map times .
  Map.elems $ schedule sc
Run Code Online (Sandbox Code Playgroud)

对我来说,这是相当可读的,因为我识别那里的模式,我可以从下到上跟踪数据流.(注意:这就是为什么所有Map函数都将a Map作为最后一个参数而不是第一个参数.在设计Haskell API时请记住这一点!)

例如,这种Map.toList . Map.fromListWith f模式很常见,很难在单行中看到它(至少对我而言).代码并不是那么复杂,它只是通过外科手术删除了它.

但是,可读性是主观的.

你将开发一种易于阅读的风格.随着您对语言的理解越来越多,风格将会发展,您对"美丽代码"的感觉也会不断发展.让它发生,并尽量不要陷入太多的重写,你不再有乐趣.