サーチ…


備考

これらの言語拡張は、グラスゴー・ハスケル・コンパイラ(GHC)を使用している場合には、通常、承認されたHaskell 2010言語レポートの一部ではないため、利用可能です。これらの拡張機能を使用するには、 フラグを使用してコンパイラに通知するか、ファイル内のmoduleキーワードの前にLANGUAGEプログラムを置く必要があります。公式文書はGCHユーザーガイドのセクション7にあります。

LANGUAGEプログラムの形式は{-# LANGUAGE ExtensionOne, ExtensionTwo ... #-}です。それはリテラル{-#後にLANGUAGE続いて、その後にコンマで区切られた拡張子のリスト、そして最後には#-}です。複数のLANGUAGEプログラムを1つのファイルに配置することができます。

MultiParamTypeClasses

これは、複数の型パラメータを持つ型クラスを可能にする非常に一般的な拡張です。 MPTCは型間の関係として考えることができます。

{-# LANGUAGE MultiParamTypeClasses #-}

class Convertable a b where
    convert :: a -> b

instance Convertable Int Float where
    convert i = fromIntegral i

パラメータの順序は重要です。

MPTCはタイプファミリに置き換えられることがあります。

FlexibleInstances

通常のインスタンスには、

All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.

つまり、 [a]のインスタンスを作成することはできますが、 [Int]インスタンスを作成することはできません。 FlexibleInstances緩和します:

class C a where

-- works out of the box
instance C [a] where

-- requires FlexibleInstances
instance C [Int] where

オーバーロードされた文字列

通常、Haskellの文字列リテラルはString型( [Char]型名)です。これは小規模な教育プログラムにとっては問題ではありませんが、実際のアプリケーションではTextByteStringなどの効率的なストレージが必要になることがよくあります。

OverloadedStringsは、リテラルのタイプを

"test" :: Data.String.IsString a => a

そのような型を予期する関数に直接渡せるようにします。多くのライブラリは、 [Char]よりも時間と空間の点で優れているData.TextData.ByteStringなどの文字列型のインタフェースを実装しています。

また、 Postgresql-simpleライブラリのようなOverloadedStringsいくつかのユニークな使い方があります。これは、SQLクエリを通常の文字列のように二重引用符で書き込むことができますが、SQLインジェクション攻撃の悪名高いソースである不適切な連結に対して保護します。

IsStringクラスのインスタンスを作成するには、 fromString関数をfromString必要があります。例

data Foo = A | B | Other String deriving Show

instance IsString Foo where
  fromString "A" = A
  fromString "B" = B
  fromString xs  = Other xs

tests :: [ Foo ]
tests = [ "A", "B", "Testing" ]

この例題は、Lyndon Maydwell(GitHubのsordina)のsordinaを受けています

TupleSections

セクション形式でタプルコンストラクタ(演算子)を適用する構文拡張。

(a,b) == (,) a b

-- With TupleSections
(a,b) == (,) a b == (a,) b == (,b) a

N-タプル

また、2より大きいアリティを持つタプルに対しても機能します

(,2,) 1 3 == (1,2,3)

マッピング

これは、セクションが使用されている他の場所で役に立ちます:

map (,"tag") [1,2,3] == [(1,"tag"), (2, "tag"), (3, "tag")]

この拡張子を除いた上記の例は、次のようになります。

map (\a -> (a, "tag")) [1,2,3]

UnicodeSyntax

特定のビルトイン演算子と名前の代わりにUnicode文字を使用できる拡張機能。

ASCII Unicode 使用する
:: 型を持っている
-> 関数型、lambda、 caseブランチなど
=> クラスの制約
forall 明示的多型
<- do表記
* タイプの種類(または種類)( Int :: ★
>- Arrows proc表記
-< Arrows proc表記
>>- Arrows proc表記
-<< Arrows proc表記

例えば:

runST :: (forall s. ST s a) -> a

になる

runST ∷ (∀ s. ST s a) → a

ことに注意してください*例は若干異なります。ので、 *予約されていない、 も同じように動作します*乗算に、または指定した任意の他の機能(*)およびその逆。例えば:

ghci> 2 ★ 3
6
ghci> let (*) = (+) in 2 ★ 3
5
ghci> let (★) = (-) in 2 * 3
-1

バイナリリテラル

標準Haskellは、(任意の接頭辞なし)は、小数の整数リテラルを記述することを可能にする進数(が先行0xまたは0X )、及び(が先行進0o又は0O )。 BinaryLiterals拡張子はバイナリ( 0bまたは0B前に付いています)のオプションを追加します。

0b1111 == 15     -- evaluates to: True

ExistentialQuantification

これは存在量化、または、他の言葉で、唯一のランタイムでインスタンスを取得型の変数を持っているタイプを可能型システムの拡張機能です。

実在型の値は、OO言語の抽象ベースクラスの参照と似ています。つまり、containsの正確な型はわかりませんが、型のクラスを制約できます。

data S = forall a. Show a => S a

GADT構文を使用すると、次のようになります。

{-# LANGUAGE GADTs #-}
data S where
   S :: Show a => a -> S

存在するタイプは、ほぼ異種のコンテナのようなものへの扉を開きます:上記のように、実際にはS値にはさまざまなタイプがありますが、それらはすべてshowすることができます。

instance Show S where
    show (S a) = show a   -- we rely on (Show a) from the above

このようなオブジェクトのコレクションを作成することができます:

ss = [S 5, S "test", S 3.0]

これはまた、多態的な振る舞いを使うことを可能にします:

mapM_ print ss

存在者は非常に強力ですが、ハスケルではあまり頻繁に必要ではないことに注意してください。上記の例では、 Showインスタンスで実際に行うことができるのは、値を表示する(つまり、文字列表現を作成する)ことだけです。したがって、 Sタイプ全体は、表示する文字列と同じくらい多くの情報を含んでいます。したがって、通常は、文字列をすぐに格納する方が良いです。特に、Haskellが遅延しているため、文字列は最初は未評価のサンクだけになります。

一方、存在感はいくつかのユニークな問題を引き起こす。例えば、タイプ情報が存在感の中で「隠される」方法。 S値をパターンマッチさせると、スコープに含まれる型(より正確にはShowインスタンス)になりますが、この情報はスコープをエスケープすることはできません。そのため、「秘密の社会」の一部になります。コンパイラその型が既に外部から知られている値を除いて、スコープからエスケープすることはできません。これは、 Couldn't match type 'a0' with '()' 'a0' is untouchableような奇妙なエラーにつながります。


一般的なパラメトリック多形性とは対照的ですが、これは一般にコンパイル時に解決されます(フルタイプの消去が可能です)。


Existential型はRank-N型とは異なります。これらの拡張子は、おおまかに言えば互いに重複しています。実在型の値を実際に使用するには、例のshowような(多分束縛された)多型関数が必要です。多型関数は普遍的に定量化されています。つまり、与えられたクラスの任意のに対して機能しますが、実在の定量化は先験的に未知の特定の型に対して機能ます。多{-# LANGUAGE Rank2Types #-}場合は、引数などの多相関数を渡すのには十分ですが、 {-# LANGUAGE Rank2Types #-}が必要です。

genShowSs :: (∀ x . Show x => x -> String) -> [S] -> [String]
genShowSs f = map (\(S a) -> f a)

ラムダケース

\arg -> case arg of代わりに\arg -> case arg of \caseを書くことを可能にする構文拡張。

次の関数定義を考えてみましょう。

dayOfTheWeek :: Int -> String
dayOfTheWeek 0 = "Sunday"
dayOfTheWeek 1 = "Monday"
dayOfTheWeek 2 = "Tuesday"
dayOfTheWeek 3 = "Wednesday"
dayOfTheWeek 4 = "Thursday"
dayOfTheWeek 5 = "Friday"
dayOfTheWeek 6 = "Saturday"

関数名を繰り返さないようにするには、次のように記述します。

dayOfTheWeek :: Int -> String
dayOfTheWeek i = case i of
    0 -> "Sunday"
    1 -> "Monday"
    2 -> "Tuesday"
    3 -> "Wednesday"
    4 -> "Thursday"
    5 -> "Friday"
    6 -> "Saturday"

LambdaCase拡張を使用すると、引数に名前を付けることなく、関数式として書くことができます:

{-# LANGUAGE LambdaCase #-}

dayOfTheWeek :: Int -> String
dayOfTheWeek = \case
    0 -> "Sunday"
    1 -> "Monday"
    2 -> "Tuesday"
    3 -> "Wednesday"
    4 -> "Thursday"
    5 -> "Friday"
    6 -> "Saturday"

RankNTypes

次のような状況を想像してみてください。

foo :: Show a => (a -> String) -> String -> Int -> IO ()
foo show' string int = do
   putStrLn (show' string)
   putStrLn (show' int)

ここでは、値をStringに変換し、その関数を文字列パラメータとintパラメータの両方に適用して両方を出力する関数を渡したいと考えています。私の心の中で、これが失敗する理由はありません!私たちは渡している両方の型のパラメータに作用する関数を持っています。

残念ながら、これはタイプチェックを行いません! GHCは、関数本体での最初のオカレンスに基づいてa型を推定します。すなわち、我々がヒットするとすぐ:

putStrLn (show' string)

GHCはその推測されますshow' :: String -> Stringするので、 stringあるString 。それはshow' intshow' intしようとしている間に爆破を進めます。

RankNTypesでは、代わりに、次のように型シグニチャを記述し、 show'型を満たすすべての関数を定量化できます。

foo :: (forall a. Show a => (a -> String)) -> String -> Int -> IO ()

これはランク2の多型です。私たちは、 show'機能は、私たちの機能の中のすべてaのために機能しなければならないと主張しています。

RankNTypes拡張では、型シグネチャのforall ...ブロックを任意に入れ子にすることができます。換言すれば、ランクNの多型を可能にする。

オーバーロードリスト

GHC 7.8で追加されました

似OverloadedLists、 OverloadedStringsは 、次のようにリストリテラルは脱糖することができます:

[]          -- fromListN 0 []
[x]         -- fromListN 1 (x : [])
[x .. ]     -- fromList (enumFrom x)

これはSetVectorMapなどの型を扱うときに便利です。

['0' .. '9']             :: Set Char
[1 .. 10]                :: Vector Int
[("default",0), (k1,v1)] :: Map String Int
['a' .. 'z']             :: Text

GHC.Exts IsListクラスは、この拡張で使用するためのものです。

IsListは、1つのタイプの関数Itemと3つの関数fromList :: [Item l] -> ltoList :: l -> [Item l]fromListN :: Int -> [Item l] -> l fromListNはオプションです。典型的な実装は:

instance IsList [a] where
  type Item [a] = a
  fromList = id
  toList   = id

instance (Ord a) => IsList (Set a) where
  type Item (Set a) = a
  fromList = Set.fromList
  toList   = Set.toList

OverloadedLists - GHCの

機能的な依存性

引数a、b、c、およびxを持つ複数パラメータの型クラスがある場合、この拡張により、型xをa、b、およびcから一意に識別できることを表現できます。

class SomeClass a b c x | a b c -> x where ...

そのようなクラスのインスタンスを宣言すると、他のすべてのインスタンスと照合して、関数の依存関係が保持されていることを確認します。つまり、 abcは同じでxは異なるインスタンスは存在しません。

複数の依存関係をコンマ区切りリストで指定できます。

class OtherClass a b c d | a b -> c d, a d -> b where ...

たとえば、MTLでは次のように見えます。

class MonadReader r m| m -> r where ...
instance MonadReader r ((->) r) where ...

あなたがタイプの値を持っている場合今、 MonadReader a ((->) Foo) => a 、コンパイラはそれを推測することができますa ~ Foo 2番目の引数は完全に最初に決定し、以来、それに応じて種類を簡素化します。

SomeClassクラスは、 xという結果をもたらす引数abc関数として考えることができます。そのようなクラスは、型システムにおける計算を行うために使用できます。

GADTs

従来の代数的データ型は、その型変数においてパラメトリックである。たとえば、ADTのように定義すると

data Expr a = IntLit Int 
            | BoolLit Bool 
            | If (Expr Bool) (Expr a) (Expr a)

IntLit :: Int -> Expr aの型がIntLit :: Int -> Expr a定量化されているため、これは静的に非型付き条件をIntLit :: Int -> Expr aすることを期待していますが、期待どおりに動作しません: a 任意の選択に対してa Expr a 。具体的には、のためにa ~ Bool 、我々は持っているIntLit :: Int -> Expr Bool 、私たちのようなもの構築することができ、 If (IntLit 1) e1 e2のタイプのものですIfコンストラクタは除外しようとしていたが。

一般化された代数的データ型は、データ構築子の結果の型をパラメトリックでないように制御することができます。 Expr型をGADTとして次のように書き直すことができます:

data Expr a where
  IntLit :: Int -> Expr Int
  BoolLit :: Bool -> Expr Bool
  If :: Expr Bool -> Expr a -> Expr a -> Expr a

ここで、コンストラクタIntLit型はInt -> Expr Intであるため、 IntLit 1 :: Expr Boolは型検査を行いません。

GADT値のパターンマッチングにより、返されたタームのタイプが洗練されます。たとえばExpr aようにExpr a評価Expr aを書くことができます。

crazyEval :: Expr a -> a
crazyEval (IntLit x) = 
   -- Here we can use `(+)` because x :: Int
   x + 1 
crazyEval (BoolLit b) = 
   -- Here we can use `not` because b :: Bool
   not b
crazyEval (If b thn els) = 
  -- Because b :: Expr Bool, we can use `crazyEval b :: Bool`.
  -- Also, because thn :: Expr a and els :: Expr a, we can pass either to 
  -- the recursive call to `crazyEval` and get an a back
  crazyEval $ if crazyEval b then thn else els 

我々が使用することができることに注意してください(+)上記の定義であるためとき例えばIntLit xマッチしたパターンは、我々はまた、学ぶことであるa ~ Int (と同様のためnotif_then_else_ときa ~ Bool )。

ScopedTypeVariables

ScopedTypeVariablesを使用すると、宣言内の普遍的な数値型を参照できます。より明示的にするには:

import Data.Monoid

foo :: forall a b c. (Monoid b, Monoid c) => (a, b, c) -> (b, c) -> (a, b, c)
foo (a, b, c) (b', c') = (a :: a, b'', c'')
    where (b'', c'') = (b <> b', c <> c') :: (b, c)

重要なことは、 bbcを使用しa 、宣言の部分式( where句のタプルと最終結果の最初のaでコンパイラに指示できることです。実際には、 ScopedTypeVariablesは複雑な関数を部品の合計として記述するのを支援し、プログラマーは具体的な型を持たない中間値に型シグネチャーを追加することができます。

PatternSynonyms

パターン同義語は、関数が式の抽象化であるのと同様のパターンの抽象化です。

この例では、 Data.Sequenceインターフェイスのインターフェイスを見て、パターンシノニムを使ってData.Sequenceを改善する方法を見てみましょう。 Seq型は、内部的に、 複雑な表現を使用してさまざまな操作、特にO(1)(un)consingと(un)snocingの両方で良好な漸近的複雑さを達成するデータ型です。

しかし、この表現は扱いにくく、その不変量のいくつかはHaskellの型システムで表現できません。このため、 Seq型は抽象型としてユーザーに公開され、不変のままのアクセサーとコンストラクター関数も保持されます。

empty :: Seq a

(<|) :: a -> Seq a -> Seq a
data ViewL a = EmptyL | a :< (Seq a)
viewl :: Seq a -> ViewL a

(|>) :: Seq a -> a -> Seq a 
data ViewR a = EmptyR | (Seq a) :> a 
viewr :: Seq a -> ViewR a

しかし、このインターフェイスを使用するのはちょっと面倒かもしれません:

uncons :: Seq a -> Maybe (a, Seq a)
uncons xs = case viewl xs of
    x :< xs' -> Just (x, xs')
    EmptyL -> Nothing

私たちはビューパターンを使って幾分それを掃除することができます:

{-# LANGUAGE ViewPatterns #-}

uncons :: Seq a -> Maybe (a, Seq a)
uncons (viewl -> x :< xs) = Just (x, xs)
uncons _ = Nothing

PatternSynonyms言語拡張を使用すると、パターンマッチングでコンスまたはスヌークリストを持つふりをすることができます。

{-# LANGUAGE PatternSynonyms #-}
import Data.Sequence (Seq)
import qualified Data.Sequence as Seq

pattern Empty :: Seq a
pattern Empty <- (Seq.viewl -> Seq.EmptyL)

pattern (:<) :: a -> Seq a -> Seq a
pattern x :< xs <- (Seq.viewl -> x Seq.:< xs)

pattern (:>) :: Seq a -> a -> Seq a
pattern xs :> x <- (Seq.viewr -> xs Seq.:> x)

これにより、非常に自然な形でunconsを書くことができます:

uncons :: Seq a -> Maybe (a, Seq a)
uncons (x :< xs) = Just (x, xs)
uncons _ = Nothing

RecordWildCards

RecordWildCardsを参照してください



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow