Haskell Language
유형 신청
수색…
소개
TypeApplications
는 컴파일러가 주어진 표현식에 대해 유형을 추론하려고 할 때 주석 을 입력하는 대신 사용할 수 있습니다.
이 일련의 예제에서는 TypeApplications
확장 프로그램의 용도 및 사용 방법을 설명합니다.
소스 파일의 맨 위에 {-# LANGUAGE TypeApplications #-}
를 입력하여 확장 기능을 활성화하는 것을 잊지 마십시오.
형식 주석 피하기
모호성을 피하기 위해 유형 주석을 사용합니다. 유형 응용 프로그램은 동일한 목적으로 사용될 수 있습니다. 예를 들어
x :: Num a => a
x = 5
main :: IO ()
main = print x
이 코드에는 모호한 오류가 있습니다. 우리는 알고 a
가 Num
인스턴스를, 그것을 인쇄하기 위해 우리는 그것이 필요로 알고 Show
인스턴스를. 경우에 작동 할 수 이었다, 예를 들어, a
Int
때문에 오류를 해결하기 위해 우리는 유형 약어를 추가 할 수 있습니다
main = print (x :: Int)
유형 응용 프로그램을 사용하는 또 다른 솔루션은 다음과 같습니다.
main = print @Int x
이것이 우리가 print
의 유형 서명을 볼 필요가 있다는 것을 이해하는 것입니다.
print :: Show a => a -> IO ()
이 함수는 a
형 a
매개 변수 하나를 사용하지만 실제로 두 개의 매개 변수를 사용한다는 것을 알 수있는 또 다른 방법이 있습니다. 첫 번째 매개 변수는 형식 매개 변수이고 두 번째 매개 변수는 형식이 첫 번째 매개 변수 인 값입니다.
값 매개 변수와 형식 매개 변수의 가장 큰 차이점은 후자의 매개 변수가 호출 할 때 함수에 암시 적으로 제공된다는 것입니다. 누가 그들을 제공합니까? 타입 추론 알고리즘! TypeApplications
가 우리에게 TypeApplications
수있는 것은 이러한 유형 매개 변수를 명시 적으로 제공하는 것입니다. 이것은 타입 유추가 올바른 타입을 결정할 수없는 경우에 특히 유용합니다.
위의 예를 분해하려면
print :: Show a => a -> IO ()
print @Int :: Int -> IO ()
print @Int x :: IO ()
다른 언어로 응용 프로그램을 입력하십시오.
Java, C # 또는 C ++ 및 generics / templates의 개념에 익숙하다면이 비교가 유용 할 것입니다.
C #에서 제네릭 함수가 있다고 가정 해 보겠습니다.
public static T DoNothing<T>(T in) { return in; }
이 함수를 float
호출하려면 DoNothing(5.0f)
하거나 명시 적으로 DoNothing<float>(5.0f)
이라고 말할 수 있습니다. 꺾쇠 괄호 안의 해당 부분은 유형 응용 프로그램입니다.
Haskell에서는 형식 매개 변수가 호출 사이트 에서뿐만 아니라 정의 사이트에서도 암시 적이라는 점만 제외하면 동일합니다.
doNothing :: a -> a
doNothing x = x
ScopedTypeVariables
, Rank2Types
또는 RankNTypes
확장을 사용하여 명시 적으로 만들 수도 있습니다.
doNothing :: forall a. a -> a
doNothing x = x
그런 다음 호출 사이트에서 doNothing 5.0
또는 doNothing @Float 5.0
다시 작성할 수 있습니다.
매개 변수 순서
타입 인자가 암시적인 문제는 우리가 하나 이상을 가지면 명확 해집니다. 그들이 어떤 순서로 들어 옵니까?
const :: a -> b -> a
작성합니까 const @Int
의미 a
동일 Int
, 또는이다 b
? 우리가 명시 적으로 const :: forall a b. a -> b -> a
과 같은 forall
사용하여 형식 매개 변수를 명시 const :: forall a b. a -> b -> a
이면 순서는 다음과 같습니다. a
, b
.
그렇지 않으면 변수의 순서는 왼쪽에서 오른쪽입니다. 언급 된 첫 번째 변수는 첫 번째 유형 매개 변수이고, 두 번째 변수는 두 번째 유형 매개 변수입니다.
두 번째 형식 변수를 지정하고 첫 번째 형식 변수를 지정하지 않으려면 어떻게해야합니까? 우리는 첫 번째 변수에 대해 와일드 카드를 사용할 수 있습니다.
const @_ @Int
이 표현식의 유형은 다음과 같습니다.
const @_ @Int :: a -> Int -> a
애매한 유형과의 상호 작용
크기가 바이트 인 클래스 유형을 도입한다고 가정 해 보겠습니다.
class SizeOf a where
sizeOf :: a -> Int
문제는 크기가 해당 유형의 모든 값에 대해 일정해야한다는 것입니다. 우리는 실제로 sizeOf
함수가 a
에 의존하기를 원하지는 sizeOf
, 그것의 유형에만 의존하기를 원합니다.
유형 응용 프로그램이 없으면 우리가 가진 최상의 솔루션은 다음과 같이 정의 된 Proxy
유형입니다.
data Proxy a = Proxy
이 유형의 목적은 유형 정보를 전달하지만 가치 정보는 전달하지 않는 것입니다. 그러면 우리 수업은 다음과 같이 보일 수 있습니다.
class SizeOf a where
sizeOf :: Proxy a -> Int
이제 궁금해 할 것입니다. 왜 첫 번째 논쟁을 모두 포기하지 않으시겠습니까? 우리 함수의 타입은 sizeOf :: Int
이거나, 클래스의 메쏘드 인 sizeOf :: SizeOf a => Int
이거나 좀 더 명확한 sizeOf :: forall a. SizeOf a => Int
이기 때문에보다 정확 sizeOf :: forall a. SizeOf a => Int
.
문제는 형식 유추입니다. 어딘가에 sizeOf
작성하면, 추론 알고리즘은 Int
기대한다는 것을 알게됩니다. 내가 대체 할 유형을 아무 생각이 없습니다 a
. 이 때문에 {-# LANGUAGE AllowAmbiguousTypes #-}
확장을 활성화 하지 않으면 컴파일러에서 정의가 거부됩니다. 이 경우 정의가 컴파일되고 모호성 오류가 발생하지 않으면 아무 데나 사용할 수 없습니다.
다행히도, 타입 어플리케이션의 도입으로 하루를 절약 할 수 있습니다! 이제 sizeOf @Int
를 작성할 수 있습니다. 명시 적으로 a
가 Int
임을 나타 a
. 유형 응용 프로그램 은 함수 의 실제 매개 변수에 나타나지 않더라도 유형 매개 변수를 제공 할 수 있습니다 !