수색…


소개

TypeApplications 는 컴파일러가 주어진 표현식에 대해 유형을 추론하려고 할 때 주석 을 입력하는 대신 사용할 수 있습니다.

이 일련의 예제에서는 TypeApplications 확장 프로그램의 용도 및 사용 방법을 설명합니다.

소스 파일의 맨 위에 {-# LANGUAGE TypeApplications #-} 를 입력하여 확장 기능을 활성화하는 것을 잊지 마십시오.

형식 주석 피하기

모호성을 피하기 위해 유형 주석을 사용합니다. 유형 응용 프로그램은 동일한 목적으로 사용될 수 있습니다. 예를 들어

x :: Num a => a
x = 5

main :: IO ()
main = print x

이 코드에는 모호한 오류가 있습니다. 우리는 알고 aNum 인스턴스를, 그것을 인쇄하기 위해 우리는 그것이 필요로 알고 Show 인스턴스를. 경우에 작동 할 수 이었다, 예를 들어, a Int 때문에 오류를 해결하기 위해 우리는 유형 약어를 추가 할 수 있습니다

main = print (x :: Int)

유형 응용 프로그램을 사용하는 또 다른 솔루션은 다음과 같습니다.

main = print @Int x

이것이 우리가 print 의 유형 서명을 볼 필요가 있다는 것을 이해하는 것입니다.

print :: Show a => a -> IO ()

이 함수는 aa 매개 변수 하나를 사용하지만 실제로 두 개의 매개 변수를 사용한다는 것을 알 수있는 또 다른 방법이 있습니다. 첫 번째 매개 변수는 형식 매개 변수이고 두 번째 매개 변수는 형식이 첫 번째 매개 변수 인 값입니다.

값 매개 변수와 형식 매개 변수의 가장 큰 차이점은 후자의 매개 변수가 호출 할 때 함수에 암시 적으로 제공된다는 것입니다. 누가 그들을 제공합니까? 타입 추론 알고리즘! 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 를 작성할 수 있습니다. 명시 적으로 aInt 임을 나타 a . 유형 응용 프로그램 은 함수실제 매개 변수에 나타나지 않더라도 유형 매개 변수를 제공 할 수 있습니다 !



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow