2013-08-30 10 views
9

Tôi đang cố gắng mô hình hóa một số dữ liệu kiểu đa hình trong haskell. Tôi hiểu tại sao mã sau không hoạt động, nhưng tôi hy vọng nó minh họa những gì tôi đang cố gắng làm. Câu hỏi của tôi là: một cách thành ngữ để mô hình hóa điều này với Haskell là gì? (Bạn không cần phải giữ định dạng đầu vào giống nhau nếu có cách nào tốt hơn - Tôi không có bất kỳ mã hoặc dữ liệu hiện có nào.)Làm thế nào để mô hình hóa một danh sách đa hình trong Haskell?

data Running = Sprint | Jog deriving (Show) 
data Lifting = Barbell | Dumbbell deriving (Show) 

data Time = Time Integer deriving (Show) 
data Pounds = Pounds Integer deriving (Show) 

data TimedActivity = TimedActivity Running Time deriving (Show) 
data WeightedActivity = WeightedActivity Lifting Pounds deriving (Show) 

class Activity a 

instance Activity TimedActivity 
instance Activity WeightedActivity 

-- I have a list of activities 
main :: IO() 
main = putStrLn $ show [ TimedActivity Sprint (Time 10) 
         , WeightedActivity Barbell (Pounds 100) 
         ] 

-- I then want to apply functions to generate summaries and 
-- reports from those activities, i.e.: 
extractLifts :: (Activity x) => [x] -> [WeightedActivity] 
extractTimes :: (Activity x) => [x] -> [TimedActivity] 
+0

Lý do đầu tiên tại sao nó không hoạt động là vì bạn đang thiếu một vài 'ở cuối' hoạt động lớp a' và cả hai 'instance Activity' dòng. Nhưng tôi đang làm việc trên một câu trả lời thực sự ngay bây giờ. – bheklilr

Trả lời

3

Câu trả lời ngắn gọn là nếu bạn muốn danh sách đa hình, sử dụng Python.

Câu trả lời dài là Haskell được thiết kế cố ý không để thực hiện việc này. Có nhiều cách để có đầy đủ các "danh sách", nhưng chúng rất khó để làm việc với và không hiệu quả hơn. Bạn sẽ không thể sử dụng các phương thức danh sách thông thường trên chúng, vì chúng sẽ không thuộc loại [a]. Nếu bạn muốn kết hợp hai loại dữ liệu, loại Either rất thuận tiện và có nhiều chức năng tích hợp, nhưng bạn càng thêm nhiều loại, chữ ký của bạn càng trở nên bất tiện hơn. Nguyên tắc chung là nếu bạn đang cố gắng xây dựng danh sách đa hình, bạn đang làm điều gì đó sai. Haskell có cách tốt để đóng gói các loại với các loại dữ liệu đại số.

Sự cố với mã cụ thể này là trong khi cả hai WeightedActivityTimedActivity là các trường hợp Activity, danh sách vẫn phải bao gồm một trường hợp duy nhất là Activity. Danh sách loại Activity a => [a] không cho biết bạn có thể kết hợp các hoạt động khác nhau, mà đúng hơn là tất cả các thành viên trong danh sách, tất cả đều là Activity s, là cùng một loại của Activity. Bạn không thể có danh sách Int s và Doubles vì chúng là các loại khác nhau, mặc dù cả hai loại này đều có Num trường hợp.

Thay vào đó, bạn có thể kết hợp TimedActivity, WeightedActivity, và Activity thành một kiểu dữ liệu duy nhất là

data Activity 
    = TimedActivity Running Time 
    | WeightedActivity Lifting Pounds 
    deriving (Show) 

Và sau đó nếu bạn có các hoạt động mới để thêm vào, bạn chỉ có thể thêm chúng vào loại Activity dữ liệu. Sau đó, các hàm extract của bạn thực sự dễ dàng để viết với đối sánh mẫu.

+7

'Câu trả lời ngắn gọn là nếu bạn muốn danh sách đa hình, hãy sử dụng Python' ... bạn có nghiêm túc không;) – Ankur

+1

@Ankur Hoàn toàn nghiêm túc. Tôi chưa bao giờ có nhu cầu có danh sách đa hình thực sự trong Haskell. Bên cạnh đó, điểm là gì? Chắc chắn, bạn có thể sử dụng existentials để làm điều này, nhưng những gì bạn đang thực sự làm là tạo ra một kiểu bao bọc các loại khác, nó không thực sự đa hình. Chỉ có một số ít các trường hợp _ever_ thực sự cần chúng, vì vậy tôi xem xét nó có mùi mã nếu bạn đang cố gắng giải quyết vấn đề với chúng. – bheklilr

+1

@ bheklilr: Giống như tất cả mọi thứ, các loại tồn tại có vị trí của chúng. Nếu bạn sử dụng chúng như là một thay thế cho các bộ sưu tập không đồng nhất kiểu OO, có thể đó không phải là lý tưởng. Nhưng ví dụ, tại nơi làm việc, chúng ta có một thư viện cần tập trung lưu trữ các kết quả của các yêu cầu đến các nguồn dữ liệu khác nhau (không đồng nhất) - cho rằng, các kiểu tồn tại chính xác là điều đúng. –

8

Ví dụ cụ thể của bạn, bạn có thể sử dụng một Either để thống nhất cả hai loại trong cùng một danh sách:

both :: [Either TimedActivity WeightedActivity] 
both = [ Left $ TimedActivity Sprint (Time 10) 
     , Right $ WeightedActivity Barbell (Pounds 100) 
     ] 

extractLifts :: [Either TimedActivity WeightedActivity] -> [WeightedActivity] 
extractLifts = rights 

extractTimes :: [Either TimedActivity WeightedActivity] -> [TimedActivity] 
extractTimes = lefts 

Trong hơn hai loại, chỉ cần xác định riêng kiểu dữ liệu trừu tượng của bạn để thống nhất họ:

data Multiple = Case1 Type1 | Case2 Type2 | ... 

... và khai thác các chức năng như vậy:

extractCase1 :: [Multiple] -> [Type1] 
extractCase1 ms = [t1 | Case1 t1 <- ms] 
+0

Giải pháp này không mở rộng tốt. "Activity API" của bạn nên biết tất cả "uptypes" "a priori". Thực sự nó "không phải là" một danh sách đa hình (là một loại duy nhất với một sản phẩm chéo của các loại). (Tôi đang chờ đợi cuốn sách của bạn!;) – josejuan

+0

Tôi khá chắc chắn đây là những gì tôi muốn bất kể, vì tôi chỉ có một số lượng nhỏ các loại ưu tiên mà tôi quan tâm. Sẽ có một vở kịch vào cuối tuần để chắc chắn. –

+0

@josejuan Tôi đang giải quyết văn bản của vấn đề chứ không phải tiêu đề. :) Tôi sẽ bắt đầu làm việc trên cuốn sách của tôi sau khi tôi hoàn thành tiến sĩ của mình. –

0

extractLifts :: (Activity x) => [x] -> [WeightedActivity]

Dòng này cho biết bạn đang cố gắng xây dựng một hàm có thể khớp với một loại yếu tố riêng lẻ trong danh sách (có thể lọc các loại khác của chúng tôi). Tôi không nghĩ rằng bạn có thể làm điều đó mà không đi hacky.

(Tôi cho rằng, dòng đó có nghĩa là extractLifts :: [(Activity x) => x] -> [WeightedActivity] - sự khác biệt tinh tế là phiên bản của bạn "chọn loại x" một lần cho toàn bộ danh sách, vì vậy tất cả các phần tử trong danh sách đều cùng loại, cùng một trường hợp Hoạt động, trong khi phiên bản thứ hai sẽ chọn loại cho từng thành phần riêng lẻ)

+1

Điều đó không chỉ cần phần mở rộng ngôn ngữ, nhưng cũng không hoạt động. – firefrorefiddle

+0

Tôi không có nghĩa là nó sẽ làm việc, tôi chỉ chỉ ra ý nghĩa của chữ ký có lẽ thậm chí còn chung chung hơn. –