2012-05-27 27 views
5

Vì vậy, tôi đang làm việc trên một thử nghiệm thú vị trong ý nghĩa theo ngữ cảnh và tôi đang chạy vào một bức tường. Tôi đang cố gắng để xác định một loại dữ liệu có thể là một nguyên thủy hoặc một chức năng mà chuyển đổi từ một nhà xây dựng khác.Ràng buộc Constructors trong một chữ ký

data WeaponPart = 
    WInt Int | 
    WHash (Map.Map String Int) | 
    WNull | 
    WTrans (WeaponPart -> WeaponPart) 

instance Show WeaponPart where 
    show (WInt x) = "WInt " ++ (show x) 
    show (WHash x) = "WHash " ++ (show x) 
    show (WTrans _) = "WTrans" 
    show WNull = "WNull" 

cold :: WeaponPart -> WeaponPart 
cold (WInt x) = WHash (Map.singleton "frost" x) 
cold (WHash x) = WHash $ Map.insertWith (+) "frost" 5 x 
cold (WTrans x) = cold $ x (WInt 5) 
cold (WNull) = cold $ (WInt 5) 

ofTheAbyss :: WeaponPart -> WeaponPart 
ofTheAbyss (WTrans x) = x (WTrans x) 

Những vấn đề là chữ ký cho ofTheAbyss cho phép bất kỳ WeaponPart như một cuộc tranh cãi, trong khi tôi chỉ muốn cho phép lập luận WTrans-constructred. Bạn có thể thấy tôi đã chỉ viết một mẫu phù hợp cho trường hợp đó.

Tôi đã thử làm việc với GADT nhưng tôi sợ rằng đó là một lỗ thỏ. Không bao giờ có thể khiến họ làm những gì tôi muốn. Có ai có bất kỳ ý tưởng làm thế nào tôi có thể thực thi chỉ đối số WTrans vào ofTheAbyss? Hay tôi hoàn toàn thiếu một cái gì đó.

Cảm ơn.

nhất, Erik

Trả lời

10

Bạn có thể thực hiện loại điều này với GADT. Có thể là từ tôi để đánh giá liệu kết quả là một lỗ thỏ, nhưng hãy để tôi ít nhất là hiển thị các công thức. Tôi đang sử dụng tiện ích mở rộng mới PolyKinds nhưng bạn có thể quản lý ít hơn.

Trước tiên, hãy quyết định loại nội dung nào bạn sẽ cần và xác định loại dữ liệu của các loại đó.

data Sort = Base | Compound 

Tiếp theo, xác định dữ liệu của bạn được lập chỉ mục theo loại của chúng. Nó giống như xây dựng một ngôn ngữ đánh máy nhỏ.

data WeaponPart :: Sort -> * where 
    WInt :: Int ->         WeaponPart Base 
    WHash :: Map.Map String Int ->     WeaponPart Base 
    WNull ::           WeaponPart Base 
    WTrans :: (Some WeaponPart -> Some WeaponPart) -> WeaponPart Compound 

Bạn có thể đại diện cho ‘ dữ liệu của bất kỳ loại ’ qua lượng hiện sinh, như sau:

data Some p where 
    Wit :: p x -> Some p 

Lưu ý rằng x không trốn thoát, nhưng chúng tôi vẫn có thể kiểm tra bằng chứng ‘ ’ rằng x ‘ thỏa mãn ’ p. Lưu ý rằng Some phải là loại data, không phải là newtype làm đối tượng GHC tồn tại newtype s.

Bạn hiện được tự do viết Sort -hoạt động chung.Nếu bạn có đầu vào chung, bạn chỉ có thể sử dụng đa hình, có hiệu quả currying Some p -> ...forall x. p x -> ....

instance Show (WeaponPart x) where 
    show (WInt x) = "WInt " ++ (show x) 
    show (WHash x) = "WHash " ++ (show x) 
    show (WTrans _) = "WTrans" 
    show WNull  = "WNull" 

Tính tồn tại là cần thiết cho Sort -generic output: tại đây tôi sử dụng nó cho đầu vào và đầu ra.

cold :: Some WeaponPart -> Some WeaponPart 
cold (Wit (WInt x)) = Wit (WHash (Map.singleton "frost" x)) 
cold (Wit (WHash x)) = Wit (WHash $ Map.insertWith (+) "frost" 5 x) 
cold (Wit (WTrans x)) = cold $ x (Wit (WInt 5)) 
cold (Wit WNull)  = cold $ Wit (WInt 5) 

Tôi phải thêm liên lạc thường xuyên Wit về địa điểm, nhưng đó là cùng một chương trình.

Trong khi đó, bây giờ chúng ta có thể viết

ofTheAbyss :: WeaponPart Compound -> Some WeaponPart 
ofTheAbyss (WTrans x) = x (Wit (WTrans x)) 

Vì vậy, nó không phải là khủng khiếp để làm việc với các hệ thống kiểu nhúng. Đôi khi có một chi phí: nếu bạn muốn ngôn ngữ được nhúng của mình có subsorting, bạn có thể thấy bạn thực hiện thêm tính toán chỉ để thay đổi chỉ mục của một số loại dữ liệu, không có sự khác biệt nào với dữ liệu. Nếu bạn không cần thuê bao, kỷ luật phụ thường có thể là một người bạn thực sự.

+0

Đây là một câu trả lời tuyệt vời. Tôi có nghĩa là cách tôi đang sử dụng GADT là một lỗ thỏ nhưng một người nào đó, rõ ràng, khôn ngoan hơn nhiều có thể hiểu được nó. Và ánh sáng của tôi, ai đó đã làm! Tuyệt vời. Tôi không bao giờ có thể có được làm thế nào để có nó cả hai cách, viz để có WeaponPart Compound và Some WeaponPart. Ngoài ra, đây là một ví dụ tuyệt vời về các hệ thống kiểu nhúng được lưu trong ngày. –

+0

Một điều cuối cùng, tôi nhận được nó làm việc rất hào hứng với tôi. Điều duy nhất tôi phải thay đổi là tôi phải lấy phần PolyKinds và tạo ra 'data Sort = Base | Hợp chất 'chỉ vào' dữ liệu Base' và 'dat Compound'. Nó tiếp tục phàn nàn, đúng là Base không phải là một nhà xây dựng kiểu. Có điều gì tôi đang thiếu với PolyKinds? Rất có thể. –

+2

Ahh, tôi sẽ để lại sai lầm của tôi cho người khác để học bằng cách: Tôi cần các pragma DataKinds, không phải là pragma PolyKinds. Tất cả các phần mở rộng mới lạ mắt này. –

1

Bạn đang cố gắng hạn chế chức năng của bạn không theo loại, nhưng bởi constructor. Đó không phải là một điều có thể làm được. Thật vậy, nó không phải là một điều có thể làm được - nếu bạn đang viết một chức năng khác, và bạn có một số không rõ WeaponPart, bạn phải có khả năng chuyển nó đến ofTheAbyss hay không - điều đó phải đánh máy hoặc không phải.

Hai tùy chọn tôi có thể nghĩ đến là:

a) Cho ofTheAbyss loại (WeaponPart -> WeaponPart) -> WeaponPart, "giải nén" các nhà xây dựng.

b) Có ofTheAbyss cung cấp lỗi thời gian chạy trên bất kỳ hàm tạo nào khác.

ofTheAbyss :: WeaponPart -> WeaponPart 
ofTheAbyss (WTrans x) = x (WTrans x) 
ofTheAbyss _ = error "Illegal argument to ofTheAbyss was not a WTrans" 
3

Đây là một giải pháp khả thi khác: chia nhỏ loại dữ liệu thành hai. Tôi đã sử dụng tên phù hợp với các câu trả lời khác để làm cho nó dễ dàng để xem song song.

data WeaponPartBase 
    = WInt Int 
    | WHash (Map.Map String Int) 
    | WNull 

data WeaponPartCompound = WTrans (WeaponPart -> WeaponPart) 
data WeaponPart = Base WeaponPartBase | Compound WeaponPartCompound 

cold :: WeaponPart -> WeaponPart 
cold (Base (WInt x)) = Base (WHash (Map.singleton "frost" x)) 
cold (Base (WHash x)) = Base (WHash $ Map.insertWith (+) "frost" 5 x) 
cold (Base WNull) = cold (Base (WInt 5)) 
cold (Compound (WTrans x)) = cold (x (Base (WInt 5)) 

ofTheAbyss :: WeaponPartCompound -> WeaponPart 
ofTheAbyss (WTrans x) = x (WCompound (WTrans x)) 

Điều này có thể được thực hiện một chút thuận tiện hơn bằng cách tuyên bố một lớp học cho những điều cơ bản:

class Basic a where 
    wint :: Int -> a 
    whash :: Map.Map String Int -> a 
    wnull :: a 

class Compounded a where 
    wtrans :: (WeaponPart -> WeaponPart) -> a 

instance Basic WeaponPartBase where 
    wint = WInt 
    whash = WHash 
    wnull = WNull 

instance Basic WeaponPart where 
    wint = Base . wint 
    whash = Base . whash 
    wnull = Base wnull 

instance Compounded WeaponPartCompound where 
    wtrans = WTrans 

instance Compounded WeaponPartCompound where 
    wtrans = Compound . wtrans 

để ví dụ coldofTheAbyss có thể trông giống như thế này:

cold' (Base (WInt x)) = whash (Map.singleton "frost" x) 
cold' (Base (WHash x)) = whash $ Map.insertWith (+) "frost" 5 x 
cold' (Base WNull) = cold' (wint 5) 
cold' (Compound (WTrans x)) = cold' (x (wint 5)) 

ofTheAbyss' (WTrans x) = x (wtrans x) 
+0

Đây cũng là một giải pháp gọn gàng. Nó là thú vị có bao nhiêu cách khác nhau bạn có thể da mèo như xa như các loại đi. Tôi tự hỏi những lợi thế và bất lợi là để làm điều này là so với GADTs. –

+0

@ErikHinton Lợi thế chính là H2010, vì vậy nó có khả năng làm việc trên các trình biên dịch khác. Nhược điểm chính là bản mẫu thêm mà nó yêu cầu: kết hợp mẫu phức tạp hơn, và tôi đếm gần 25 dòng xác định thuần túy và thực hiện các lớp kiểu 'Basic' và' Compounded'. –

Các vấn đề liên quan