購読中です 読者をやめる 読者になる 読者になる

Haskellの言語拡張たち

前回、なんか拡張を沢山使っていたが、おまじないのままなのもそろそろまずい感じなので、とりあえず調べてみた。

今回調べた拡張

  • 型系
    • GADTs
    • ScopedTypeVariables
    • EmptyDataDecls
    • TypeFamilies
  • 型クラス系
    • MultiParamTypeClasses
    • TypeSynonymInstances
    • FlexibleInstances
    • FlexibleContexts
  • パターン系
    • BangPatterns
    • PatternGuards
    • ViewPatterns
  • 文字列系
    • OverloadedStrings
  • TemplateHaskell系
    • TemplateHaskell
    • QuasiQuotes
  • import系
    • ImplicitPrelude
    • PackageImports
  • 他言語系
    • CPP
    • ForeignFunctionInterface

設定方法

hsファイルの上の方に、

{-# LANGUAGE 〜 #-}

な感じで書くか、実行時に

$ ghc -X〜 ...

$ ghci -X〜 ...

$ runhaskell -X〜 ...

な感じで指定する。


無効にする場合は、No〜 を指定する。


調べてるときに使った GHC は 7.0.4 。

型系

GADTs

代数的データ型のもつ型変数に対して、型を限定した型構築子が書ける。

{-# LANGUAGE GADTs #-}

-- GADTs 拡張が必要
data List a where
  INil :: List Int
  CNil :: List Char
  Cons :: a -> List a -> List a
ScopedTypeVariables

スコープを持つ明示的な型変数 (forall) を書ける。

-- x と y の型が違うのでエラーになる
myId :: a -> a
myId x = y
 where
  y :: a
  y = x

{-# LANGUAGE ScopedTypeVariables #-}

-- ScopedTypeVariables 拡張が必要
myId :: forall a. a -> a
myId x = y
 where
  y :: a
  y = x
EmptyDataDecls

構築子のないデータ型の宣言を書ける。

{-# LANGUAGE EmptyDataDecls #-}

-- EmptyDataDecls 拡張が必要
data EmpData
data EmpDataF a

何も指定しなくても通ったので、デフォルトで有効のよう。
試しに NoEmptyDataDecls を指定したら、コンパイラに EmptyDataDecls を指定しろといわれた。

TypeFamilies

型族やデータ族が書ける。

{-# LANGUAGE TypeFamilies #-}

-- 型族
-- TypeFamilies 拡張が必要
type family TypeConv a :: *
type instance TypeConv EmpData = Int 
type instance TypeConv (EmpDataF a) = a 

-- データ族
-- TypeFamilies 拡張が必要
data family MyDF a
data instance MyDF Int = MyDF1 Int | MyDF2 Int Int
newtype instance MyDF EmpData = MyDF3 Bool
newtype instance MyDF (EmpDataF a) = MyDF4 a

型クラス系

MultiParamTypeClasses

型パラメータを複数持つ型クラスが書ける。

{-# LANGUAGE MultiParamTypeClasses #-}

-- MultiParamTypeClasses 拡張が必要
class Set s a where
  empty :: s a
  insert :: a -> s a -> s a
  member :: a -> s a -> Bool
TypeSynonymInstances

型シノニム (typeで宣言した型) に対して、インスタンス宣言が書ける。

{-# LANGUAGE TypeSynonymInstances #-}

class CHoge a where
  hoge :: a -> a

type SHoge = String
-- TypeSynonymInstances 拡張が必要
instance CHoge SHoge where
  hoge = id
FlexibleInstances

型引数を持つ型に、特定の型を与えたものに対して、インスタンス宣言が書ける。

{-# LANGUAGE FlexibleInstances #-}

class CHoge a where
  hoge :: a -> a

-- FlexibleInstances 拡張が必要
instance CHoge (Maybe Int) where
  hoge _ = Nothing
FlexibleContexts

型パラメータを複数持つ型クラス制約が書ける。

{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-}

-- MultiParamTypeClasses 拡張が必要
class CCHoge a b where
  choge :: a -> b

-- FlexibleContexts 拡張が必要
ihoge :: (CCHoge a Int) => a -> Int
ihoge _ = 0

パターン系

BangPatterns

!(パターン) で、束縛時にそのパターンを正格評価する。

{-# LANGUAGE BangPatterns #-}

-- BangPatterns 拡張が必要
myseq :: a -> b -> b
myseq !x y = y
PatternGuards

パターンを含むガードが書ける。

{-# LANGUAGE PatternGuards #-}

-- PatternGuards 拡張が必要
justOddOrElse :: Maybe Int -> Int -> Int
justOddOrElse x y
 | Just a <- x, odd a = a
 | otherwise = y

これもデフォルトで有効のよう。

ViewPatterns

パターンの中で関数を呼んで、その結果に対してマッチするパターンマッチ (関数 -> パターン) が書ける。

{-# LANGUAGE ViewPatterns #-}

import Control.Monad (mfilter)

-- ViewPatternt 拡張が必要
justOddOrElse :: Maybe Int -> Int -> Int
justOddOrElse (mfilter odd -> Just a) _ = a
justOddOrElse _ b = b

文字列系

OverloadedStrings

文字列リテラルの型を、Stringではなく、(IsString a) => a にする。

{-# LANGUAGE OverloadedStrings #-}

import Data.ByteString (ByteString)
import Data.ByteString.Char8 () -- for IsString ByteString

-- OverloadedStrings 拡張が必要
hogebstr :: ByteString
hogebstr = "hoge"

TemplateHaskell系

TemplateHaskell

TemplateHaskell の構文が使える。

コンパイル時メタプログラミングとかできるようになる。

QuasiQuotes

準クォート ([〜| 〜 |]) が使える。

Yesod の [persist| 〜 |] とか [hamlet| 〜 |] とか。

import系

ImplicitPrelude

Prelude を暗黙的にインポートする。
デフォルトで有効。

NoImplicitPrelude を指定することで、暗黙的に Prelude が import されないようにできる。

PackageImports

パッケージ名で修飾したimport宣言 (import "(パッケージ名)" ~) を書ける。

他言語系

CPP

Cプリプロセッサの構文が使える。

#define とか #if とか #include とか。

ForeignFunctionInterface

他言語関数インタフェース (FFI) を有効にする。
デフォルトで有効らしい。