수색…


비고

이러한 언어 확장은 일반적으로 Glasgow Haskell 컴파일러 (GHC)를 사용할 때 승인 된 Haskell 2010 언어 보고서의 일부가 아니므로 사용할 수 있습니다. 이러한 확장을 사용하려면 플래그를 사용하여 컴파일러에 알리거나 파일의 module 키워드 앞에 LANGUAGE programa를 두어야합니다. 공식 문서는 GCH 사용자 가이드의 7 절 에서 찾을 수 있습니다.

LANGUAGE 프로그램의 형식은 {-# LANGUAGE ExtensionOne, ExtensionTwo ... #-} 입니다. 그것은 리터럴 {-# 뒤에 LANGUAGE 뒤에 확장자가 쉼표로 구분 된 목록이오고 마지막으로 닫는 #-} 입니다. 여러 LANGUAGE 프로그램을 하나의 파일에 둘 수 있습니다.

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 유형을가집니다.이 String[Char] 의 유형 별칭입니다. 소규모 교육 프로그램에서는 문제가되지 않지만 실제로는 Text 또는 ByteString 과 같은보다 효율적인 저장이 필요합니다.

OverloadedStrings 단순히 리터럴 유형을

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

이러한 유형을 예상하는 함수에 직접 전달할 수 있습니다. 많은 라이브러리가 [Char] 보다 특정 시간 및 공간 이점을 제공하는 Data.TextData.ByteString 을 비롯한 문자열 유형에 대해이 인터페이스를 구현합니다.

또한 SQL 쿼리를 일반적인 문자열처럼 큰 따옴표로 쓰지 만 SQL 연결 공격의 악의적 인 소스 인 부적절한 연결에 대한 보호 기능을 제공하는 Postgresql-simple 라이브러리의 것과 같은 OverloadedStrings 고유 한 용도가 있습니다.

IsString 클래스의 인스턴스를 만들려면 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 )이 여기 에서 찾았 습니다 .

TupleSections

섹션 방식으로 튜플 생성자 (연산자)를 적용 할 수있는 구문 확장입니다.

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

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

N- 튜플

또한 2보다 큰 아티 (arity)를 갖는 튜플

(,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

특정 내장 연산자와 이름 대신 유니 코드 문자를 사용할 수있는 확장 프로그램입니다.

ASCII 유니 코드 사용
:: 유형이있다
-> 함수 유형, 람다, 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

이진 리터럴

표준 하스켈은 정수 (접두어없이) 진수 리터럴 16 진수 (앞에 쓸 수 있습니다 0x 또는 0X )와 진수 (앞에 0o 또는 0O ). BinaryLiterals 확장은 바이너리 옵션 ( 0b 또는 0B 앞에 붙임)을 추가합니다.

0b1111 == 15     -- evaluates to: True

ExistentialQuantification

이것은 단지 런타임 †에서 인스턴스화 형태 변수가, 존재 적 즉, 정량화, 또는되는 유형을 할 수있는 종류의 시스템 확장입니다.

실존 타입의 값은 객체 지향 언어의 abstract-base-class 레퍼런스와 유사합니다 : 당신은 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]

또한 다형성 (polymorphic) 동작을 사용할 수 있습니다.

mapM_ print ss

Existentials는 매우 강력 할 수 있지만, Haskell에서는 실제로 필요하지 않습니다. 위 예제에서 Show 인스턴스로 실제로 할 수있는 것은 문자열 표현을 만드는 것입니다. 따라서 전체 S 유형은 표시 할 때 얻은 문자열만큼 정확하게 정보를 포함합니다. 따라서 일반적으로 문자열을 바로 저장하는 것이 좋습니다. 특히 하스켈이 게으르므로 문자열은 처음에는 평가되지 않은 썽크가됩니다.

반면에, 존재는 몇 가지 독특한 문제를 일으킨다. 예를 들어, 유형 정보가 실존 적으로 "숨겨진"방식. S 값을 패턴 매치하면 범위에 (더 정확하게는 Show 인스턴스에 포함 된) 유형이 포함되지만이 정보는 절대 범위를 벗어날 수 없으므로 컴파일러는 약간의 "비밀 사회"가됩니다. 외부에서 타입이 이미 알려진 값을 제외하고는 아무 것도 이스케이프하지 않습니다. 이것은 Couldn't match type 'a0' with '()' 'a0' is untouchable 이상한 오류가 발생할 Couldn't match type 'a0' with '()' 'a0' is untouchable 있습니다.


이것을 일반적인 매개 변수 다형성과 대조합니다.이 다형성은 일반적으로 컴파일시 해결됩니다 (전체 유형 삭제 허용).


Existential 타입은 Rank-N 타입과는 다르다.이 확장은 대략적으로 말하자면 서로 이중이다. 실존 타입의 값을 실제로 사용하기 위해서는 예제에서 show 와 같은 (아마도 제한된) 다형 함수가 필요하다. 다형 함수는 보편적으로 정량화됩니다. 즉, 주어진 클래스의 모든 유형에 대해 작동 합니다 . 반면에 실존 적 정량화는 선험적으로 알려지지 않은 특정 유형에 대해 작동 함을 의미합니다. 다형 함수가있는 경우 인수로 다형 함수를 전달하는 것으로 충분하지만 {-# 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 A는 String . show' intshow' int 하려고 시도하는 동안 폭발합니다.

RankNTypes 사용하면 다음과 같이 유형 서명을 작성하고 show' 유형을 충족시키는 모든 함수를 수량화 할 수 있습니다.

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

우리는 것을 주장하는이 계급이 다형성입니다 show' 기능은 모든 일을해야 우리의 함수 내에서의, 이전 구현이 작동합니다. a

RankNTypes 확장은 형식 서명에서 forall ... 블록을 임의로 중첩 할 수있게합니다. 즉, N 랭크의 다형성을 허용합니다.

오버로드 목록

GHC 7.8에 추가됨 .

유사 OverloadedLists, OverloadedStrings는 다음과 같이 목록 리터럴이 desugared 할 수 있습니다 :

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

이것은 Set , VectorMap 과 같은 유형을 처리 할 때 편리합니다.

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

IsList 의 클래스 GHC.Exts 이 확장과 함께 사용하기위한 것입니다.

IsList 에는 하나의 유형 함수 Item 과 세 개의 함수, 즉 fromList :: [Item l] -> l , toList :: 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

오버로드 목록 에서 가져온 예제 - 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 유추 할 수 있습니다. 두 번째 인수가 첫 번째 인수를 완전히 결정하므로 그에 따라 형식을 단순화합니다.

SomeClass 클래스는 x 를 결과로하는 인수 abc 의 함수로 생각할 수 있습니다. 이러한 클래스는 타입 시스템에서 계산을 수행하는 데 사용될 수 있습니다.

GADT

기존의 대수 데이터 유형은 유형 변수에서 매개 변수입니다. 예를 들어, ADT를 정의하면

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

이 정적이 아닌 잘 입력 조건문을 배제 것이라는 희망으로,이 유형 때문에 예상대로 작동하지 않습니다 IntLit :: Int -> Expr a 선택을 위해 : universially 정량화 ,이 유형의 값을 생성 a Expr a . 특히 a ~ Bool 경우 IntLit :: Int -> Expr Bool 을 사용하여 If (IntLit 1) e1 e2 와 같은 것을 만들 수 있습니다. If 생성자는 If 생성자의 형식을 배제하려고 시도했습니다.

Generalized Algebraic Data Types를 사용하면 데이터 생성자의 결과 유형을 매개 변수가 아닌 단순한 형식으로 제어 할 수 있습니다. Expr 유형을 GADT로 다시 작성할 수 있습니다.

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

여기서 IntLit 생성자의 IntLitInt -> Expr Int 이므로 IntLit 1 :: Expr Bool 은 형식 검사를하지 않습니다.

GADT 값의 패턴 일치는 반환되는 용어의 유형을 상세하게 만듭니다. 예를 들어, 다음과 같이 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 (또한 a ~ Boolnotif_then_else_ 대해서도)를 배우기 때문에 위의 정의에서 (+) 를 사용할 수 있습니다.

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)

중요한 것은 a , bc 를 사용하여 선언의 하위 표현 ( where 절의 튜플과 최종 결과의 첫 번째 a 에서 컴파일러에 지시 할 수 있다는 것입니다. 실제로 ScopedTypeVariables 는 복잡한 함수를 파트의 합으로 작성하는 것을 도와 주므로 프로그래머는 구체 유형이없는 중간 값에 유형 시그니처를 추가 할 수 있습니다.

PatternSynonyms

패턴 동의어 는 함수가 표현식을 추상화하는 것과 유사한 패턴의 추상화입니다.

이 예제에서는 Data.Sequence 인터페이스의 인터페이스를 살펴보고 패턴 동의어를 사용하여 데이터를 향상시킬 수있는 방법을 살펴 보겠습니다. Seq 유형은 내부적으로 다양한 작업, 특히 O (1) (un) consing 및 (un) snocing 모두에 대해 좋은 점근 적 복잡성을 달성하기 위해 내부적으로 복잡한 표현 을 사용하는 데이터 유형입니다.

그러나이 표현은 다루기 힘들고 그 불변량의 일부는 하스켈의 형식 체계에서 표현 될 수 없다. 이 때문에 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 언어 확장을 사용하면 패턴 매칭을 통해 우리가 생각 나게하는 snoc-list가 있다고 가정하여 더 멋진 인터페이스를 제공 할 수 있습니다.

{-# 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