2013-09-26 1 views
19

이 왜 ghci에 내가 입력 할 수있는 것을 함수 내에서 플로트 :하스켈, 지능을 곱한

5.0 * (3 - 1) 
> 10.0 

을하지만 시도하는 경우와 .hs 파일에서 함수를 만들고 그것을로드 :

test :: Float -> Int -> Int -> Float 
test a b c = a * (b - c) 

오류가 있습니까? "예상 유형 'Float'과 유추 된 유형 'Int'를 일치시킬 수 없습니다. 하나의 부동 소수점과 2 개의 정수 인수를 사용하여 위의 연산을 수행하는 함수는 어떻게 작성합니까?

ghci v6을 사용하고 있습니다. 그 차이를 만드는 경우 .12.1 ...

답변

37

숫자 리터럴 (즉, 하스켈 코드에 숫자를 입력하는 것)은 일부 고정 유형이 아닙니다. 그것들은 다형성입니다. 그들은 어떤 맥락에서 그들이 구체적인 유형을 요구하는 평가를 받아야합니다.

그래서 식 5.0 * (3 - 1)Float하여 Int 승산 하지이다. 5.0Fractional 유형이어야하며, 31은 각각 Num 유형입니다. 3 - 1은 3과 1 모두가 동일 Num 유형이어야한다는 것을 의미하지만, 우리는 여전히 어떤 특정한 제약 조건을 가지고 있지 않습니다. 빼기의 결과는 같은 유형입니다.

*은 두 인수가 모두 같은 형식이어야하며 결과도 같은 형식이어야 함을 의미합니다. 5.0Fractional 유형이므로 (3 - 1)도 입력해야합니다. 3, 1(3 - 1)Num 유형이어야하지만 Fractional 유형은 모두 Num 유형이기 때문에이 요구 사항은 충돌하지 않습니다.

결과적 전체 표현식이 5.0 * (3 - 1)Fractional 일부 타입, 및 5.0, 31 모두 동일한 타입이다. 당신이 볼 수 GHCi의 :t 명령을 사용할 수 있습니다 : 실제로 그 표현을 평가하기 위해

Prelude> :t 5.0 * (3 - 1) 
5.0 * (3 - 1) :: Fractional a => a 

하지만, 우리는 몇 가지 구체적인 유형 그렇게해야합니다.이것을 평가하여 Float, Double 또는 다른 특정 Fractional 유형을 필요로하는 함수에 전달하면 하스켈은 그 중 하나를 선택합니다. 특정 유형이어야하는 다른 컨텍스트없이 표현식을 평가하는 경우 Haskell은 자동으로 하나의 기본 규칙을 선택합니다 (기본 규칙이 적용되지 않으면 모호한 유형 변수에 대한 유형 오류가 발생합니다). 내가 5.0 * (3 - 1)을 평가 한 위

Prelude> 5.0 * (3 - 1) 
10.0 
Prelude> :t it 
it :: Double 

, 다음 GHCi는 항상 평가 된 마지막 값에 바인딩 마법 it 변수의 유형에 대해 물었다. 이것은 GHCi가 내 Fractional a => a 유형을 Double으로 기본값으로 설정하여 표현식의 값이 10.0임을 계산합니다. 이 평가를 수행 할 때 Double 초 만 곱셈 (그리고 빼기) 한 결과 DoubleInt을 곱하지 않았습니다.


자, 당신은 서로 다른 종류의 수 있습니다처럼 여러 숫자 리터럴 할 때 무슨 일인지입니다. 그러나 귀하의 test 함수는 리터럴을 곱하는 것이 아니라, 알려진 유형의 곱셈 변수입니다. Haskell에서 IntFloat을 곱할 수 없습니다. * 연산자는 Num a => a -> a -> a 유형을 가지고 있기 때문에 동일한 숫자 유형의 두 값을 취하여 그 유형의 결과를 제공합니다. IntInt을 곱하면 , Float이면 Float이되고 Float이됩니다. IntFloat을 곱하면 ???이됩니다.

다른 언어는 경우에 따라 변환 함수에 대한 호출을 암시 적으로 삽입하여 이러한 종류의 작업을 지원합니다. Haskell never은 형식을 암시 적으로 변환하지만 변환 함수가 있습니다. 원할 경우 명시 적으로 호출하면됩니다. 당신이 서명을 추가하기 전에,

test :: Float -> Int -> Int -> Float 
test a b c = a * fromIntegral (b - c) 
3

당신은 수레를 곱 전에 정수에 fromIntegral를 사용해야합니다.

GHCI에서 http://www.haskell.org/haskellwiki/Converting_numbers

, 숫자가 수레로 간주되지 않았거나 int를 사용하기 전까지는 (예 : 실행시). REPL 스타일 개발에 더 좋습니다.

적절한 컴파일러에서 자동 강제 변환이 없습니다. 곱셈을 사용하면 두 값이 곱셈을 지원하는 형식 클래스에 속해야한다고 가정합니다. 예 : int를 곱하거나 부동 소수점을 곱합니다. 다른 명시 적으로 형식화 된 함수를 사용하지 않았으므로 int로 간주합니다. 그 가정은 당신의 (선택적인) 유형 서명과 다릅니다.

+2

"적절한 컴파일러에서는 자동 강제 변환이 없습니다."- 그러나 숫자 리터럴의 강요가 있습니다 ("ghci"와 동일). – fjarri

+0

아, 맞아. 리터럴 강제가 여기에 작용하지 않습니다. 내가 편집 할게. –

+0

실제로 컴파일 된 모듈보다 GHCi에서 부동 소수점 또는 정수로 간주되는 숫자가 문제가 될 가능성이 훨씬 큽니다. 두 경우 모두 숫자의 유형을 파악할 때 전체 "편집 단위"에서 숫자가 사용되는 방식이 고려되지만 GHCi에서 "편집 단위"는 한 번에 하나씩 취한 각 입력 행입니다. 따라서 숫자 형이 기본값으로 설정되는 방식은 REPL 스타일 개발을 위해 수행되지 않습니다. 실제로 REPL에서는 꽤 고통 스럽습니다. – Ben

3

귀하의 test 기능은보다 일반적인했다 :이 트릭을 할 것입니다 당신은 그것을 제한 할 수

> let test a b c = a * (b - c) 
> :t test 
    test :: Num a => a -> a -> a -> a 

을하지만, 모든 유형이 동일해야합니다 :

test :: Fractional a => a -> a -> a -> a -- some real types 
test :: Integral a => a -> a -> a -> a -- all integer types 
test :: Float -> Float -> Float -> Float 
test :: Int -> Int -> Int -> Int 

test :: Int -> Float -> Float -> Float --wrong 

한편, 2Int이 아니며 0.2Float이 아니므로 gchi :

> :t 2 
2 :: Num a => a 
> :t 0.2 
0.2 :: Fractional a => a 
4

는 시도 대신에 다음

test :: Float -> Int -> Int -> Float 
test a b c = a * fromIntegral (b - c) 

왜이 일을 하는가?

    b 이후
  1. c 모두 Int의 발현 (b - c)는 또한 Int이다이다.
  2. (*)의 유형 서명은 Num a => a -> a -> a입니다.
  3. aFloat입니다. 하스켈은 (a*)의 유형 서명을 Float -> Float으로 업데이트합니다.
  4. (b - c)Int이고 이 아니기 때문에 하스켈은 a * (b - c)을 시도하면 불평 할 것입니다.
  5. fromIntegral의 유형 서명은 (Integral a, Num b) => a -> b입니다. 따라서 fromIntegral (b - c)의 유형 서명은 Num b => b입니다. Float 이후
  6. (*)의 유형 서명이 Num a => a -> a -> a 때문에 당신이 a * fromIntegral (b - c)을 할 수있는 Num typeclass의 인스턴스입니다.
  7. 결과는 test의 형식 서명에서 Float으로 평가됩니다.

희망이 도움이되었습니다.