美丽而可怕的Haskell

时间:2012-06-20 21:55:25

标签: haskell functional-programming

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

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))))))))

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

1 个答案:

答案 0 :(得分:18)

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

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

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

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

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

但是,可读性是主观的。

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