2013-08-07 32 views
7

Ý định của tôi rất đơn giản. Tôi muốn bọc các chức năng của loại a -> b vào String -> String (để một loạt các hàm không đồng nhất có thể được đưa vào danh sách). Vì vậy, tôi viết:Chuyển đổi các chức năng của kiểu `a -> b` thành các kiểu` String -> String` trong Haskell

wrap :: (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s :: a) 

Tuy nhiên, ghc khiếu nại:

Could not deduce (Read a1) arising from a use of `read' 
from the context (Read a, Show b) 
    bound by the type signature for 
      wrap :: (Read a, Show b) => (a -> b) -> String -> String 

Tôi muốn biết lý do tại sao mảnh của tôi về mã sẽ không hoạt động và những loại hacks là cần thiết để đạt được mục tiêu của tôi?

Cảm ơn.

+3

Đặ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

+0

Đâ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

+0

@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

Trả lời

13

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.

10

Để xác định loại read s một cách rõ ràng, bạn sẽ cần một cái gì đó giống như ScopedTypeVariables:

{-# LANGUAGE ScopedTypeVariables #-} 
... 
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s :: a) 

Kể từ nếu không :: a chú thích bên trong hàm dùng để chỉ một loại khác nhau từ a trong chữ ký kiểu (nó ngầm nghĩa là :: forall a. a). Nhưng lưu ý rằng bạn cũng có thể chỉ cần thả chú thích loại hoàn toàn:

wrap :: (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s) 

Vì loại read s có thể được suy ra. Điều đó cũng cho phép bạn đơn giản hóa nội dung thành

wrap f = show . f . read 
Các vấn đề liên quan