Haskell에서 구체적인 유형의 엔티티를 전달하면 될 다형성 함수 유형을 인쇄 할 수 있습니까?
다음은 3 가지 유형의 함수 다형성입니다.
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
그리고 여기 비다 형성 함수 :
:t Data.Char.digitToInt
Data.Char.digitToInt :: Char -> Int
전자를 후자에 적용하면 한 가지 유형의 함수 다형성을 얻습니다.
:t (.) Data.Char.digitToInt
(.) Data.Char.digitToInt :: (a -> Char) -> a -> Int
어떤 수단 (.)
"인스턴스"했다 (I 아니에요 확인이 올바른 용어입니다;는 C ++ 프로그래머로, 나는 그래서 전화 것)과 b === Char
와 c === Int
의 서명, 그래서 (.)
에 적용됩니다 digitToInt
다음은
(Char -> Int) -> (a -> Char) -> a -> Int
내 질문은 : 주어진 화면에 출력이 서명을하는 방법은 무엇입니까 (.)
, digitToInt
나는 후자에 전자를 적용 할 것을 "정보"와?
관심있는 사람의 경우,이 문제는 이전의 중복으로 폐쇄되었다 이것 .
답변
다른 답변에는 asTypeOf
HTNW의 답변에 있는 함수 와 같이 인위적으로 제한된 유형으로 정의 된 함수의 도움이 필요합니다 . 다음 상호 작용이 보여 주듯이 이것은 필요하지 않습니다.
Prelude> let asAppliedTo f x = const f (f x)
Prelude> :t head `asAppliedTo` "x"
head `asAppliedTo` "x" :: [Char] -> Char
Prelude> :t (.) `asAppliedTo` Data.Char.digitToInt
(.) `asAppliedTo` Data.Char.digitToInt
:: (Char -> Int) -> (a -> Char) -> a -> Int
이것은 정의에 내포 된 람다 바인딩의 다형성 부족 을 이용합니다 asAppliedTo
. f
본문 의 두 항목 모두 동일한 유형을 지정해야하며 이것이 결과 유형입니다. const
여기에 사용 된 함수 는 자연 유형도 있습니다 a -> b -> a
.
const x y = x
의 모서리에 숨겨진이 깔끔한 작은 기능이 있습니다 Prelude
.
Prelude.asTypeOf :: a -> a -> a
asTypeOf x _ = x
"첫 번째 인수가 두 번째 인수와 동일한 유형을 갖도록 강제하는 것"으로 문서화되어 있습니다. 이것을 사용하여 (.)
의 첫 번째 인수 유형을 강제 할 수 있습니다 .
-- (.) = \x -> (.) x = \x -> (.) $ x `asTypeOf` Data.Char.digitToInt -- eta expansion followed by definition of asTypeOf -- the RHS is just (.), but restricted to arguments with the same type as digitToInt -- "what is the type of (.) when the first argument is (of the same type as) digitToInt?" ghci> :t \x -> (.) $ x `asTypeOf` Data.Char.digitToInt
\x -> (.) $ x `asTypeOf` Data.Char.digitToInt
:: (Char -> Int) -> (a -> Char) -> a -> Int
물론 이것은 당신이 필요로하는 많은 논쟁에 대해 작동합니다.
ghci> :t \x y -> (x `asTypeOf` Data.Char.digitToInt) . (y `asTypeOf` head)
\x y -> (x `asTypeOf` Data.Char.digitToInt) . (y `asTypeOf` head)
:: (Char -> Int) -> ([Char] -> Char) -> [Char] -> Int
주석에서 @KABuhr의 아이디어를 변형 한 것으로 간주 할 수 있습니다. 형식 추론을 안내하기 위해 구현보다 더 제한적인 시그니처가있는 함수를 사용하는 것입니다. 단, 우리가 직접 정의 할 필요는 없습니다. 람다 아래에 문제의 식을 복사합니다.
@HTNW의 답변이 아마도 그것을 다루고 있다고 생각하지만 완전성을 위해 inContext
솔루션이 자세히 작동 하는 방법은 다음과 같습니다 .
함수의 유형 서명 :
inContext :: a -> (a -> b) -> a
즉, 입력하고 싶은 것이 있고 그것이 사용되는 "문맥"(인수로 받아들이는 람다로 표현 가능)이 있다면 다음과 같이 말하십시오.
thing :: a1
context :: a2 -> b
다음 표현식을 구성하여 (컨텍스트의 제약 조건 )과 ( a1
일반 유형 thing
)을 강제로 통합 할 수 있습니다 a2
.
thing `inContext` context
일반적으로 통합 유형 thing :: a
은 손실되지만의 유형 서명은 inContext
이 전체 결과 표현식의 유형도 원하는 유형으로 통합 될 것임을 의미 a
하며 GHCi는 해당 표현식의 유형을 기꺼이 알려줍니다.
그래서 표현 :
(.) `inContext` \hole -> hole digitToInt
(.)
결국 지정된 컨텍스트 내에있는 유형이 할당 됩니다. 다음과 같이 다소 오해를 불러 일으킬 수 있습니다.
(.) `inContext` \(.) -> (.) digitToInt
이후 (.)
좋은과 같다 익명 람다의 인수 이름은 hole
이다. 의 최상위 정의를 숨기는 로컬 바인딩을 생성하고 (.)
있지만 여전히 동일한 이름 (세련된 유형으로)을 지정하고 있기 때문에 잠재적으로 혼란 스러울 수 있습니다. 이러한 람다 남용을 통해 원래 표현을 (.) digitToInt
그대로 쓸 수 있습니다 . 적절한 상용구로.
inContext
GHCi에 유형을 요청하는 경우 실제로 정의 방법과 관련 이 없으므로 inContext = undefined
작동 했을 것입니다. 그러나 타입 시그니처를 살펴보면 inContext
작동 정의 를 제공 하는 것은 쉽습니다 .
inContext :: a -> (a -> b) -> a
inContext a _ = a
이것은의 정의에 불과 const
하므로 inContext = const
작동합니다.
당신은 사용할 수 있습니다 inContext
한 번에 여러 가지를 입력, 그들은 이름 대신 표현 될 수 있습니다. 전자를 수용하기 위해 튜플을 사용할 수 있습니다. 후자가 작동하려면 람바에서 더 합리적인 인수 이름을 사용해야합니다.
예를 들면 다음과 같습니다.
λ> :t (fromJust, fmap length) `inContext` \(a,b) -> a . b
(fromJust, fmap length) `inContext` \(a,b) -> a . b
:: Foldable t => (Maybe Int -> Int, Maybe (t a) -> Maybe Int)
표현식 fromJust . fmap length
에서 유형이 다음과 같이 특수화되었음을 알려줍니다 .
fromJust :: Maybe Int -> Int
fmap length :: Foldable t => Maybe (t a) -> Maybe Int
TypeApplications
확장 을 사용하여이를 수행 할 수 있으며 , 유형 매개 변수를 인스턴스화하는 데 사용할 유형을 명시 적으로 지정할 수 있습니다.
λ :set -XTypeApplications
λ :t (.) @Char @Int
(.) @Char @Int :: (Char -> Int) -> (a -> Char) -> a -> Int
인수는 정확한 순서 여야합니다.
와 같은 "일반"유형 서명이있는 함수의 foo :: a -> b
경우 순서는 유형 매개 변수가 서명에 처음 나타나는 순서로 정의됩니다.
ExplicitForall
like 를 사용하는 함수의 foo :: forall b a. a -> b
경우 순서는 해당 항목에 따라 정의됩니다 forall
.
(채울 유형을 아는 것과는 반대로) 적용 (.)
을 기반으로 구체적으로 유형을 파악하고 싶다면 digitToChar
GHCi에서 할 수 없다고 확신하지만 Haskell IDE 지원을 적극 권장 할 수 있습니다.
예를 들어 다음은 VSCode에서 나를 찾는 방법입니다 (여기 에 확장자가 있습니다 ).

이것은 HTNW의 답변에 대한 사소한 변형입니다.
다형성 식별자를 포함하는 잠재적으로 큰 표현이 있다고 가정합니다. poly
.... poly ....
그 시점에서 다형성 유형이 어떻게 인스턴스화되었는지 궁금합니다.
이는 다음과 같이 GHC의 두 가지 기능 asTypeOf
(HTNW에서 언급 한대로) 및 유형이 지정된 구멍을 활용하여 수행 할 수 있습니다 .
.... (poly `asTypeOf` _) ....
_
구멍 을 읽을 때 GHC는 해당 구멍 대신 입력해야하는 용어 유형을보고하는 오류를 생성합니다. 를 사용했기 때문에 해당 컨텍스트에서 필요한 asTypeOf
특정 인스턴스의 유형과 동일해야합니다 poly
.
다음은 GHCi의 예입니다.
> ((.) `asTypeOf` _) Data.Char.digitToInt
<interactive>:11:17: error:
* Found hole: _ :: (Char -> Int) -> (a -> Char) -> a -> Int