2014-05-25 19 views
5

Tôi đã thử nghiệm với các chương trình được nhập bằng cách sử dụng thư viện Data.Singletons, theo sau việc phát triển các vectơ được chú thích theo chiều dài trong bài báo, "Lập trình được đánh máy phụ thuộc với Singletons" và tôi đã chạy vào vấn đề sau.Các đối số và loại trừ không hợp lệ

Mã này, không bao gồm định nghĩa của hàm indexI, typechecks trong GHC 7.6.3 và các công trình như mong đợi trong sự vắng mặt của nó:

{-# LANGUAGE DataKinds #-} 
{-# LANGUAGE GADTs #-} 
{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE TemplateHaskell #-} 
{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE UndecidableInstances #-} 

import Data.Singletons 
import Data.Singletons.TH 

data Nat where 
    Z :: Nat 
    S :: Nat -> Nat 
    deriving Eq 

$(genSingletons [''Nat]) 

data FlipList :: * -> * -> Nat -> * where 
    Cons :: ((a -> b) -> a -> b) -> FlipList a (a -> b) n -> FlipList a b (S n) 
    Nil :: FlipList a b Z 

type family (m :: Nat) :< (n :: Nat) :: Bool 
type instance m :< Z = 'False 
type instance Z :< (S n) = 'True 
type instance (S m) :< (S n) = m :< n 

type family PreMap a b (m :: Nat) :: * 
type instance PreMap a b Z = a -> b 
type instance PreMap a b (S n) = PreMap a (a -> b) n 

type family BiPreMap a b (m :: Nat) :: * 
type instance BiPreMap a b m = PreMap a b m -> PreMap a b m 

index :: ((m :< n) ~ 'True) => SNat m -> FlipList a b n -> BiPreMap a b m 
index SZ (Cons f _) = f 
index (SS sm) (Cons _ fl) = index sm fl 

indexI :: ((m :< n) ~ 'True, SingI m) => FlipList a b n -> BiPreMap a b m 
indexI = withSing index 

Sau bao gồm indexI, GHC sản xuất hai lỗi,

Could not deduce (PreMap a b m ~ PreMap a b a0) 
    from the context ((m :< n) ~ 'True, SingI Nat m) 
     bound by the type signature for 
       indexI :: ((m :< n) ~ 'True, SingI Nat m) => 
          FlipList a b n -> BiPreMap a b m 

và,

Could not deduce (PreMap a b m ~ PreMap a b a0) 
    from the context ((m :< n) ~ 'True, SingI Nat m) 
     bound by the type signature for 
       indexI :: ((m :< n) ~ 'True, SingI Nat m) => 
          FlipList a b n -> BiPreMap a b m 

Gốc của một trong hai lỗi có vẻ là cụm từ withSing index có loại FlipList a b n -> BiPreMap a b a0 và, mà không thể suy ra a0 ~ m, GHC không thể chứng minh được BiPreMap a b m ~ BiPreMap a b a0. Tôi biết rằng kiểu suy luận về loại gia đình thiếu phần lớn các tiện ích mà chúng ta có được khi làm việc với ADTS (tiêm, phát sinh, vv), nhưng sự hiểu biết của tôi về vấn đề chính xác là gì trong trường hợp này và cách phá vỡ nó. . Có một số hạn chế tôi có thể chỉ định có thể xóa nó?

+3

Tác phẩm sau đây dành cho tôi: 'indexI :: forall m n a b. ((m: FlipList a b n -> BiPreMap a b m; indexI = withSing (chỉ mục :: SNat m -> FlipList a b n -> BiPreMap a b m) ' – user2407038

Trả lời

2

Điều bạn nên hiểu ở đây là không có gì sai với mã của bạn, nó chỉ là suy luận kiểu GHC không thể xác định được loại an toàn như thế nào. Lưu ý rằng bằng cách bình luận ra indexI, tải mã trong GHC và yêu cầu cho các loại withSing index:

*Main Data.Singletons> :t withSing index 
    withSing index 
    :: (SingI Nat a, (a :< n) ~ 'True) => 
     FlipList a1 b n -> PreMap a1 b a -> PreMap a1 b a 

Điều này có nghĩa rằng GHC có thể gõ kiểm tra mã của bạn, và nó thậm chí còn suy luận kiểu giống như những gì bạn được chỉ định (tối đa alpha-tương đương). Vậy tại sao nó không kiểm tra mã của bạn?

Vấn đề là mã của bạn không nói rõ cách các tham số loại withSing nên được khởi tạo, cụ thể là biến loại a phải được khởi tạo đến m từ chữ ký loại của bạn. Có thể hiểu rằng a nên được khởi tạo cho một thứ khác (ví dụ: [m] hoặc m -> m) để bạn triển khai withSing index để có loại bạn chỉ định. GHC không có cách nào để xác định rằng a nên được khởi tạo đến m, bạn sẽ gặp phải các lỗi mà bạn nhận được. Lưu ý rằng GHC sẽ không cố gắng đoán loại instantiations này và đó là một điều tốt. Chúng tôi không muốn ngôn ngữ cấp loại của GHC bị thoái hóa thành một thông dịch viên Prolog;). Đó là trong quan điểm của tôi một chút quá gần đó rồi.

Điều này có nghĩa là bạn có hai tùy chọn để giải quyết vấn đề của mình. Giải pháp đầu tiên được đề xuất bởi user2407038 ở trên: sử dụng chú thích kiểu để cho GHC biết tham số kiểu a của hàm withSing nên được khởi tạo như thế nào. Hãy để tôi lặp lại code của mình vào đây để tham khảo:

indexI :: forall m n a b . ((m :< n) ~ 'True, SingI m) => FlipList a b n -> BiPreMap a b m 
indexI = withSing (index :: SNat m -> FlipList a b n -> BiPreMap a b m) 

Lưu ý rằng bạn cần forall cú pháp rõ ràng trong chữ ký loại để đảm bảo rằng các m từ chữ ký kiểu là trong phạm vi trong việc thực hiện indexI (tìm kiếm các tài liệu về tiện ích ScopedTypeVariables của GHC để biết thêm thông tin).

Cách thay thế khác của bạn là thay đổi mã để GHC có thể xác định cách thực hiện a theo suy luận kiểu.Để hiểu điều này, hãy xem xét rằng GHC đang nói với bạn rằng nó không thể suy ra PreMap a b m ~ PreMap a b a0. Điều này có nghĩa là GHC đã suy ra withSing index đối với loại tôi đã cho bạn thấy ở đầu câu trả lời này và đang cố gắng tìm các loại instantiations để xác định loại suy luận này bằng loại bạn đã chú thích như thế nào. Để làm điều này, nó cố gắng giải quyết một ràng buộc bình đẳng BiPreMap a b m ~ BiPreMap a b a0, được đơn giản hóa thành ràng buộc đơn giản hơn PreMap a b m ~ PreMap a b a0. Tuy nhiên, đó là nơi nó bị mắc kẹt. Bởi vì các họ kiểu như PreMap không nhất thiết phải tiêm, nó không thể quyết định điều này là m phải bằng a0. Một cách để giải quyết vấn đề này là thay đổi BiPreMap thành kiểu dữ liệu hoặc kiểu mới. Không giống như các gia đình loại, kiểu dữ liệu và newtypes đơn ánh trong lập luận của họ và sau đó GHC có thể giải quyết những hạn chế:

newtype BiPreMap a b m = BiPreMap { getBiPreMap :: PreMap a b m -> PreMap a b m } 

index :: ((m :< n) ~ 'True) => SNat m -> FlipList a b n -> BiPreMap a b m 
index SZ (Cons f _) = BiPreMap f 
index (SS sm) (Cons _ fl) = BiPreMap (getBiPreMap (index sm fl)) 

indexI :: ((m :< n) ~ 'True, SingI m) => FlipList a b n -> BiPreMap a b m 
indexI = withSing index 

Vậy đó, tôi hy vọng điều này làm rõ một số những gì đang xảy ra ... Lưu ý rằng loại của "lập trình được đánh máy phụ thuộc vào Haskell" mà bạn đang cố gắng làm là không quan trọng trong Haskell và bạn có thể gặp phải nhiều vấn đề này trên đường đi. Rất thường xuyên, chữ ký loại rõ ràng sẽ là giải pháp cho các lỗi loại kỳ lạ mà bạn có thể gặp phải. Các ứng dụng loại rõ ràng cũng sẽ hữu ích, nhưng tôi hiểu rằng sự hỗ trợ cho chúng vẫn còn thiếu hoặc đang được tiến hành trong GHC.

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