Common Lisp vs Haskell, Chapter 2

Meta note

対象読者

前章を読了済みの者。

Introduction

本稿は「すごいH本」の内容をCommon Lispに翻訳しながらCLerがHaskellを学ぶその第2章である。 本章ではHaskellの基本的な型を、Common Lispの型システムに翻訳しながら学習する。

Common Lispはその処理系により動的型付け〜ゆるい型付けを行う言語である。 CLtL2からの引用をしよう。

1つの例外を除いて宣言は完全にオプションであり、正しい宣言は正しいプログラムの意味に影響を与えない。

それに対しHaskellは強い型付けの言語であり、型の機能が言語中枢に分かちがたく結びついている。 ちょうどそれはCommon Lispに於いてリスト機能が言語中枢に分かちがたく結びついているがごとくである。

型に関してはHaskellの方が相当強力であり、それをCommon Lisp上に再現するのは相当難しい。 そのせいもあって本章は前章と比べて大変短いものとなっている。 また、特筆すべきハイライトも皆無といって差し支えない。

2

2.1

ghci> :t 'a'
'a' :: Char
ghci> :t True
True :: Bool
ghci> :t "HELLO!"
"HELLO!" :: [Char]
ghci> :t (True, 'a')
(True, 'a') :: (Bool, Char)
ghci> :t 4 == 5
4 == 5 :: Bool

Common Lispでオブジェクトの型を知るにはTYPE-OFが使えなくもない。

cl-user> (type-of #\a)
STANDARD-CHAR
cl-user> (type-of t)
BOOLEAN
cl-user> (type-of "HELLO!")
(SIMPLE-ARRAY CHARACTER (6))

列を扱いだすと役に立たなくなる。

cl-user> (type-of '(t . #\a))
CONS
cl-user> (type-of '(= 4 5))
CONS

型を調べるのとはまた違うが、オブジェクトの中身を調べるINSPECTは標準で存在している。

cl-user> (inspect '(t . #\a))
;; The object is a CONS.
;; 0. CAR: T
;; 1. CDR: #\a
;; > :q
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z

Common Lispで関数の型を宣言するにはDECLAIMを以下のように使う。

(declaim(ftype (function(fixnum fixnum fixnum)fixnum) add-three))
(defun add-three(x y z)
  (+ x y z))

2.3

ghci> :t head
head :: [a] -> a

TYPE-OFが役に立たないのは関数でも同じ。 有益な情報は得られない。

cl-user> (type-of #'car)
FUNCTION

DESCRIBEに渡せば処理系依存で少しはまともな情報が見れる。

cl-user> (describe #'car)
;; #<FUNCTION CAR>
;;  [compiled function]
;; Lambda-list: (LIST)
;; Declared type: (FUNCTION (LIST) (VALUES T &OPTIONAL))
;; Documentation:
;;   Return the 1st object in a list.

INSPECTした場合。

cl-user> (inspect #'car)
;; The object is a FUNCTION named CAR.
;; 0. Lambda-list: (LIST)
;; 1. Ftype: (FUNCTION (LIST) (VALUES T &OPTIONAL))

2.2

Int

Common Lispに於けるFIXNUMに相当。 仕様では16ビットの符号つき整数が最低限保証されている。 最大値は処理系依存で、それらの値は定数MOST-POSITIVE-FIXNUMMOST-NEGATIVE-FIXNUMに登録されている。

Integer

Common Lispに於けるINTEGERと完全に等価と言える。

Float Double

Common Lispでは浮動小数点数は全部で4つある。 各々SHORT-FLOATSINGLE-FLOATDOUBLE-FLOATLONG-FLOATである。 これらの範囲は処理系依存であるが、SHORT-FLOATが最小固定精度であり、LONG-FLOATが最大固定精度である。 全てFLOAT型と解釈される。

Bool

Common Lispは汎ブールをサポートしているのでNIL以外の値は全てTrueである。 ただし、BOOLEANという場合はNILTのみを指すこととなる。

cl-user> (typep 0 'boolean)
NIL

Char

Common Lispに於けるCHARACTERに相当する。 CHARACTERに特定化されたベクタが文字列となる。

cl-user> (vector #\a)
#(#\a)
cl-user> (stringp *)
NIL
cl-user> (make-array 1 :initial-element #\a :element-type 'character)
"a"
cl-user> (make-string 1 :initial-element #\a)
"a"

Tuple

Common Lispのリストに相当する。 CONS-TYPE-SPCIFIERを駆使することで、長さや要素の型を指定できる。

cl-user> (typep '(1) '(cons fixnum null))
T
cl-user> (typep '(1) '(cons fixnum fixnum))
NIL
cl-user> (typep '(1 . 2) '(cons fixnum fixnum))
T
cl-user> (typep '(1 "string" #\a) '(cons fixnum (cons string (cons character null))))
T

2.3

Common Lispには型変数に相当する機能はない。 強いてあげればDEFTYPEの引数をそのようなものとして利用することが出来なくもないといった程度か。

2.4

Common Lispに型クラスに相当する機能はない。