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)
là không nhân số Int
theo số Float
. 5.0
phải là một số loại Fractional
, 3
và 1
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
và (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)
"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
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. –
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