Mã của bạn sẽ không hoạt động vì Haskell không tái sử dụng hoặc phạm vi biến loại; các a
trong wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
là hoàn toàn khác nhau a
từ một trong read s :: a
(và cả hai đều được định lượng phổ dụng). Đây là nguồn của a1
trong thông báo lỗi; GHC là alpha-chuyển đổi chương trình để
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a1)
Tuy nhiên, loại lập luận f
's được cố định bên trong wrap
, vì vậy chỉ đơn giản là loại bỏ các loại chú thích hoạt động tốt. Chức năng của bạn trở nên
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s)
-- Or wrap f = show . f . read
Và bạn có thể sử dụng nó:
ghci> map ($ "42") [wrap (+ (7 :: Integer)), wrap (* (2.0 :: Double))]
["49","84.0"]
Lưu ý rằng điều này có nghĩa rằng read s
có một kiểu bạn không thể viết xuống. Trong Haskell 2010 (hoặc 98), cách duy nhất để giải quyết vấn đề này là sử dụng một hàm như asTypeOf :: a -> a -> a
; asTypeOf
chỉ là const
, nhưng nhờ chữ ký kiểu của nó, nó ràng buộc đối số đầu tiên, được trả về của nó có cùng kiểu với thứ hai của nó. Sau đó, tất nhiên, bạn sẽ phải gợi ý một biến loại a
. Sau đây sẽ làm việc cho điều đó:
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s `asTypeOf` fInput)
where fInput = undefined
fOutput = f fInput -- I still can't give this a type signature
Trong GHC, để tránh điều này, bạn có thể bật the ScopedTypeVariables
extension; với điều đó, nếu bạn rõ ràng đủ điều kiện cho tất cả các biến loại của bạn với một forall
, chúng sẽ được sắp xếp giống như các tên mức giá trị. Sau đó, mã của bạn sẽ trở thành
{-# LANGUAGE ScopedTypeVariables #-}
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
Nhưng hãy nhớ, bạn không cần bất kỳ chú thích kiểu nào cho ví dụ đơn giản này.
Đặt một loạt các hàm không đồng nhất trong danh sách không phải là haskell tốt, và gói tất cả chúng lên trong chuyển đổi Chuỗi chắc chắn là haskell xấu. Bạn đang cố gắng làm gì theo nghĩa lớn hơn? – NovaDenizen
Đây là một sai lầm. Đừng làm thế. Bạn đang vứt bỏ hệ thống kiểu mạnh mẽ của Haskell, và tất cả việc kiểm tra thời gian biên dịch tuyệt vời đi cùng với nó. Một thợ máy nói với bạn chiếc xe của bạn là tốt và sau đó nó phá vỡ trên đường cao tốc không phải là sử dụng nhiều như một thợ cơ khí nói với bạn để chi tiêu $ 50 sửa chữa một cái gì đó (bây giờ, trong khi nó trong nhà để xe) đó sẽ gây ra bạn để phá vỡ xuống (sau này, sau khi nó gây ra nhiều thiệt hại hơn, có một khoản phí gọi và anh chàng sửa chữa nó không có phần anh ta cần với anh ta). Đánh máy tĩnh là cơ khí tốt, gõ năng động là một trong những người nói nó là tốt. – AndrewC
@NovaDenizen Tôi chắc chắn sẽ đánh máy tĩnh. Gần đây tôi đã viết một máy chủ đơn giản. 'a -> IO b' là viết tắt của một dịch vụ được triển khai. Một thành phần là để chuyển đổi '[(String, a -> IO b)]' thành 'Chuỗi bản đồ (a -> IO b)'. Nhưng hệ thống kiểu không cho phép điều đó. Tôi có nhiều kiểu thiết kế phức tạp hơn trong tâm trí của mình để thực thi an toàn kiểu (để client phải nạp đầu vào kiểu 'a' vào dịch vụ kiểu' a -> IO b' nếu không toàn bộ chương trình sẽ không gõ kiểm tra). Nhưng bắt là máy chủ có thể không phục vụ khách hàng được viết bằng Haskell. Vì vậy, cơ chế của tôi không hoạt động trong trường hợp đó. – tfboy