head関数のいくつかの定義
1つの配列を引数にとって、その先頭要素を返す関数headの定義
不正な値は実行時エラーを生じさせる
型安全でない
部分関数になっている
型が嘘をついているmrsekut.icon
実行時エラーが生じうる
code:定義.hs
head (x:_) = x
head [] = error "empty list"
code:使用.hs
let xs = head [] -- no type error, runtime error
不正な値はダミー値で表現する
空配列を適用した時は、NULLを返す
例えば以下はいずれもNILを返すが、その違いを値から読み取れない
code:lisp
(car '()) ; 不正な入力なのでNILを返す
(car '(() (1 2 3) (4 5 6)))) ; 先頭要素が空リストなのでNILを返す
(car '(NIL (1 2 3) (4 5 6)))) ; 先頭要素がNILなのでNILを返す
これらを区別したい時、追加でハックが必要になる
code:使用.lisp
(car '())
NIL
code:定義.go
func (l *List) Front() *Element {
if l.len == 0 {
return nil
}
return l.root.next
}
Haskellでやるなら、ダミー値を要求するとか
code:hs
headOr def (x:xs) = x
headOr def [] = def
返り値をMaybeにする
code:定義.hs
head [] = Nothing
head (x:_) = Just x
全域関数である
型に嘘はない
実行時エラーは起きない
この関数の利用者は、Maybeのパターンマッチしてhandlingする必要がある
明らかにJustが返ってくるような文脈でもhandlingが必要になる
ここで、fromJustを使えばhandlingせずに済むが、
しかし、そうすると結局型の嘘が生じるため、実行時エラーの危険性が再燃する
関数の提供者は楽かもしれないが、利用者は面倒
この例ではそこまで問題ではないが、上のNULLと同様の問題も生じている
Nothingの多義性
例えばCustom Preludeのrioはこの定義を提供している ref そもそも空配列を適用できないようにする
型に嘘はない
全域関数である
Maybeの時に生じたhandlingも不要
code:定義.hs
head :: NonEmpty a -> a
head (x :| _) = x
短所は、lengthなど、通常のListの関数と同様の関数を再定義する必要があること
NonEmptyの定義からMaybeの定義を作るのは容易
逆は難しい
code:hs
head' = fmap head . nonEmpty
code:hs
rev_cons :: Proof (IsCons xs) -> Proof (IsCons (Rev xs))
rev_cons = axiom
gdpHead :: (a ~~ xs ::: IsCons xs) -> a gdpHead xs = head (the xs)
-- 使用
gdpEndpts :: IO (Int, Int)
gdpEndpts = do
putStrLn "Enter a non-empty list of integers:"
xs <- readLn
name xs $ \xs-> case classify xs of
IsCons proof ->
return (gdpHead (xs ...proof),
gdpHead (gdpRev xs ...rev_cons proof))
IsNil proof -> gdpEndpts
う〜ん、これ言うほど楽なのか?mrsekut.icon
gdpHead内で使っているheadはPreludeのものなので、NonEmptyの時のように[a]と同じ関数を再定義する必要がない
篩型を用いた定義
code:hs
head :: SizeGreaterThan 0 a -> a
head = Prelude.head . unrefine