2013-09-26 156 views
19

Tại sao nó rằng trong ghci Tôi có thể nhập:Haskell, nhân Int và nổi trong vòng một hàm

5.0 * (3 - 1) 
> 10.0 

Nhưng nếu tôi cố gắng và tạo ra một chức năng trong một tập tin .hs và tải nó tại:

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

Tôi gặp phải lỗi? "Không thể khớp với kiểu được mong đợi 'Float' với kiểu được suy ra 'Int'? Và làm cách nào để tôi có thể viết một hàm có trong một dấu phẩy động và 2 đối số nguyên và thực hiện thao tác trên trên chúng?

Tôi đang sử dụng ghci v6 .12.1 nếu mà làm cho một sự khác biệt ...

Trả lời

37

Chữ số (tức là chỉ cần gõ một số trong mã Haskell) không phải là một số loại cố định. Chúng có tính đa hình. Họ cần phải được đánh giá trong một số bối cảnh đòi hỏi họ phải có một loại cụ thể.

Vì vậy, biểu thức 5.0 * (3 - 1)không nhân số Int theo số Float. 5.0 phải là một số loại Fractional, 31 là một số loại Num. 3 - 1 có nghĩa là cả 3 và 1 đều phải là loại cùng loạiNum, nhưng chúng tôi vẫn chưa có thêm bất kỳ ràng buộc nào về một loại cụ thể; kết quả của phép trừ là cùng loại.

* có nghĩa là cả hai đối số phải cùng loại và kết quả sẽ là cùng một loại. Vì 5.0 là một số loại Fractional, nên cũng phải là (3 - 1). Chúng tôi đã biết rằng 3, 1(3 - 1) phải là một số loại Num nhưng tất cả các loại Fractional cũng là loại Num, vì vậy yêu cầu này không xung đột.

Kết quả cuối cùng là toàn bộ biểu 5.0 * (3 - 1) là một số loại đó là Fractional, và 5.0, 3, và 1 đều cùng loại. Bạn có thể sử dụng lệnh :t trong GHCi để thấy điều này:

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

Nhưng để thực sự đánh giá biểu rằng, chúng ta cần phải làm như vậy đối với một số loại bê tông.Nếu chúng tôi đã đánh giá điều này và chuyển nó đến một số chức năng yêu cầu Float, Double hoặc một số loại khác cụ thể là Fractional thì Haskell sẽ chọn một chức năng đó. Nếu chúng ta chỉ đánh giá biểu thức mà không có ngữ cảnh nào khác yêu cầu nó phải là một kiểu cụ thể, Haskell có một số quy tắc mặc định để tự động chọn một quy tắc cho bạn (nếu các quy tắc mặc định không áp dụng, thay vào đó sẽ cung cấp cho bạn một lỗi kiểu về các biến kiểu mơ hồ)).

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

Trên đây tôi đã đánh giá 5.0 * (3 - 1), sau đó yêu cầu các loại của it biến kỳ diệu mà GHCi luôn liên kết với giá trị cuối cùng nó được đánh giá. Điều này cho tôi biết rằng GHCi đã đặt mặc định loại Fractional a => a của mình thành chỉ Double, để tính toán giá trị của biểu thức là 10.0. Khi thực hiện đánh giá đó, nó chỉ bao giờ nhân (và trừ) Double s, nó không bao giờ nhân với một số Double bởi một số Int.


Bây giờ, đó là những gì đang xảy ra khi bạn cố gắng nhiều số literals mà hình như họ có thể là các loại khác nhau. Nhưng hàm test của bạn không nhân các chữ cái, nó nhân các biến của các kiểu đã biết cụ thể. Trong Haskell bạn không thể nhân một số Int bởi một số Float vì toán tử * có loại Num a => a -> a -> a - phải mất hai giá trị của cùng một loại số và cho bạn kết quả là loại đó. Bạn có thể nhân một số điện thoại Int theo số Int để nhận số Int hoặc Float theo số Float để nhận số Float. Bạn không thể nhân một số Int theo số Float để nhận số ???.

Các ngôn ngữ khác chỉ hỗ trợ loại thao tác này bằng cách chèn ngầm các lệnh gọi đến các hàm chuyển đổi trong một số trường hợp. Haskell không bao giờ chuyển đổi hoàn toàn giữa các loại, nhưng nó có chức năng chuyển đổi. Bạn chỉ cần gọi cho họ một cách rõ ràng nếu bạn muốn họ được gọi. Điều này sẽ làm các trick:

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

Bạn cần phải sử dụng fromIntegral trên số nguyên trước khi nhân với các phao nổi.

http://www.haskell.org/haskellwiki/Converting_numbers

Trong GHCI, những con số không được giả định là phao hoặc ints cho đến khi bạn sử dụng chúng (ví dụ: tại thời gian chạy). ut tốt hơn cho phát triển theo kiểu REPL.

Trong trình biên dịch phù hợp, không có bất kỳ sự ép buộc tự động nào. Nó thấy phép nhân thừa nhận rằng hai giá trị phải thuộc về một loại lớp hỗ trợ phép nhân. ví dụ: nhân ints hoặc nhân các phao. Vì bạn không sử dụng bất kỳ hàm được đánh dấu rõ ràng nào khác, nó giả định ints. Giả thiết đó sau đó khác với chữ ký loại (tùy chọn) của bạn.

+2

"Trong trình biên dịch phù hợp, không có bất kỳ sự ép buộc tự động nào" - nhưng có một sự ép buộc của các chữ số, giống như trong '' ghci''. – fjarri

+0

ah, phải. và sự ép buộc theo nghĩa đen không đi vào đây. Tôi sẽ chỉnh sửa. –

+0

Bạn thực sự có nhiều khả năng gặp sự cố với các số được giả định là phao hoặc int trong GHCi so với các mô-đun được biên dịch. Trong cả hai trường hợp, cách các số được sử dụng trên toàn bộ "đơn vị biên dịch" được tính đến khi tìm ra loại số, nhưng trong GHCi "đơn vị biên dịch" là mỗi dòng đầu vào được lấy một lần! Vì vậy, cách các kiểu số được mặc định không được thực hiện vì lợi ích của việc phát triển kiểu REPL; nó thực sự khá đau đớn trong REPL. – Ben

3

chức năng test của bạn là tổng quát hơn, trước khi bạn thêm một chữ ký:

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

Bạn có thể hạn chế nó, nhưng tất cả các loại phải giống nhau:

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 

Nhân tiện, 2 không phải là Int0.2 không phải là Float, hãy yêu cầu gchi:

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

Hãy thử như sau thay vì:

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

Tại sao công việc này?

  1. Kể từ bc đều Int là biểu thức (b - c) cũng là một Int.
  2. Chữ ký loại (*)Num a => a -> a -> a.
  3. a thuộc loại Float Haskell cập nhật chữ ký loại (a*) thành Float -> Float.
  4. Tuy nhiên, kể từ (b - c)Int và không phải là Float Haskell sẽ khiếu nại nếu bạn đã thử thực hiện a * (b - c).
  5. Chữ ký loại fromIntegral(Integral a, Num b) => a -> b.
  6. Do đó, chữ ký loại fromIntegral (b - c)Num b => b.
  7. Kể từ Float là trường hợp của typeclass Num bạn được phép làm a * fromIntegral (b - c) vì chữ ký loại (*)Num a => a -> a -> a.
  8. Kết quả, từ chữ ký loại test đánh giá là Float.

Hy vọng điều này sẽ hữu ích.