この記事はLint Advent Calendar 2016の23日目の記事です
はじめに
軽い気持ちでHaskellのリントツールである HLint を紹介するよ
なかなかHaskellのコードレビューをしてもらう機会ってないと思うんだけど、こいつは結構頼りになる奴なんだ
動作も軽快だし、stack
使ってるならインストールも一瞬だよ
例
命名規則
スネークケースをキャメルケースにしろ、って
some_func x = x + 2
$ hlint snake.hs
snake.hs:1:1: Suggestion: Use camelCase
Found:
some_func x = ...
Why not:
someFunc x = ...
1 hint
do
1行の場合のdo
は不要だよ、って
check int = case int of
(Just x) -> do
putStrLn "just!"
print x
Nothing -> do
putStrLn "nothing"
main = do
check (Just 5)
$ hlint do.hs
do.hs:6:17: Warning: Redundant do
Found:
do putStrLn "nothing"
Why not:
putStrLn "nothing"
do.hs:9:8: Warning: Redundant do
Found:
do check (Just 5)
Why not:
check (Just 5)
2 hints
2箇所出てるね
そんな関数あるよ
自分で関数組み合わせちゃった場合、まんまそんなメソッドがあると教えてくれる
main = print $ take 5 $ repeat 3
$ hlint f.hs
f.hs:1:16: Warning: Use replicate
Found:
take 5 $ repeat 3
Why not:
replicate 5 3
1 hint
repeat 3
は無限に3
がつまったリストを作り、take 5
で先頭5つを取り出す、結果[3, 3, 3, 3, 3]
となる
replicate 5 3
は3
を5
回繰り返してくれるので、やはり[3, 3, 3, 3, 3]
となる
すげー!
ポイントフリースタイル
できるよ、って
左辺と右辺の最後の引数が同じ場合は省略できること
allTwice
にxs
を渡すのがmap (*2)
にxs
を渡すのと同じなら、つまりallTwice
とmap (*2)
は同じ処理、みたいな?
allTwice xs = map (*2) xs
$ hlint pfs.hs
pfs.hs:1:1: Warning: Eta reduce
Found:
allTwice xs = map (* 2) xs
Why not:
allTwice = map (* 2)
1 hint
allTwice
はmap (* 2)
です、って書くのがCoolだ
冗長な無名関数(ラムダ式)
even
は1つのInt
を受ける関数で、\x -> even x
は1つのInt
を受けてeven
に渡すので、どちらも挙動は同じなんだ
trimOdd :: [Int] -> [Int]
trimOdd xs = filter (\x -> even x) xs
$ hlint lambda.hs
lambda.hs:2:1: Warning: Eta reduce
Found:
trimOdd xs = filter (\ x -> even x) xs
Why not:
trimOdd = filter (\ x -> even x)
lambda.hs:2:16: Warning: Avoid lambda
Found:
\ x -> even x
Why not:
even
2 hints
\x -> even x
はeven
でいいだろ!って出ているし、ついでにさっきのポイントフリースタイルの警告も出ているね
trimOdd :: [Int] -> [Int]
trimOdd = filter even
これでtrimOdd
って関数はeven
でfilter
するよ、って読めるね
こんな感覚で新たな関数を元からある関数を組み合わせるだけで作る、ってのは結構ある
Data.Function.on
なんかimport
してもいないのに型が合うと「こんな関数あるけどお前なんで使わないの?」って言ってくれる
import Data.Function
data Card = Card { value :: Int, label :: String } deriving Show
sameValue x y = value x == value y
main = do
let card1 = Card 1 "foo"
let card2 = Card 1 "bar"
print $ sameValue card1 card2
$ hlint on.hs
No hints
お...およよよよよよ
警告が出なくなってる...ヽ(´o`;モシモシ?
前と同じ感じで書いてると思うんだけどなー...
ちなみに、前に見た警告は以下みたいなのでした
Found:
x y -> value x == value y
Why not:
(==) `Data.Function.on` value
なになにわからん
on
の型はなんじゃろ
ghci> :t Data.Function.on
Data.Function.on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
なんだこれー
(ちなみに知人に コナミコマンド って言われたw)
どうやらこれが
sameValue x y = value x == value y
こうできるらしい
sameValue x y = (on (==) value) x y
a
とa
を(a -> b)
でb
とb
にして、(b -> b -> c)
でc
にするんだね
2つの値両方に同じ変換をした上で、その2つを何かにするって感じ
on
はバッククオートで囲って演算子化して間に置くのがCoolです
sameValue x y = ((==) `on` value) x y
そして、いつものポイントフリースタイルで後ろの引数を2つとも消しちゃって
sameValue = (==) `on` value
こんな!
sameValue
とは両方にvalue
を適用してから(==)
するって事だよ って宣言的になったね!すてき!
終わりに
ぶっちゃけ、on
すげーーー!!!何こんなのまで言ってくれるの!!??
「なんでon
つかわねーの?」とか言うけど、そんな標準ライブラリでもない関数シラネーwww
って去年くらいの感動をちゃちゃっと書くだけのつもりだったのに、まさかそれが出ないとは...
企画倒れも良いところ...
どなたか理由等わかる方がいたらご教授くださいm(_ _)m
ただまぁちょっと気まぐれでHLint
かけるだけでも結構勉強になったりするので、
そういうことが出来るHaskellは「これマジオススメ」ってことで ノシ