2017-10-09 21 views
6

Tôi hiện đang có một cấu trúc như sau:Ngăn chặn các phương pháp mặc định lẫn nhau đệ quy từ vòng lặp tại thời gian chạy

class F a where 
    f :: ... 
    default f :: (G a...) => ... 
    f = (some definition in terms of g) 

class F a => G a where 
    g :: ... 
    default g :: (C a...) => ... 
    g = (some definition in terms of f) 

Trong tiếng Anh hy vọng phần nào đơn giản, tôi có thể viết f về g luôn. Tôi có thể viết g theo điều khoản của f đôi khi, cụ thể là khi a thỏa mãn ràng buộc C.

Vấn đề tôi thấy ở đây là nếu ai đó viết, cho biết một loại T đáp ứng C T

instance F T 
instance G T 

này sẽ biên dịch và vòng lặp khi chạy. Mặc dù cả hai định nghĩa mặc định là chính xác nhưng điều quan trọng là ít nhất một định nghĩa được xác định.

Tôi có thể giải quyết vấn đề này với MINIMAL pragma nếu fg thuộc cùng một lớp, nhưng trong trường hợp này thì không.

Cũng đặt cả fg cùng lớp dường như không thể, như khi có một định nghĩa về f cho mỗi định nghĩa của g, không có một định nghĩa về g cho mỗi định nghĩa của f. Một khả năng là di chuyển g vào F nhưng cũng đặt một ràng buộc C a vào nó, nhưng điều đó sẽ ngăn tôi xác định g với định nghĩa không mặc định cho bất kỳ a nào không đáp ứng C a.

Có cách nào để tổ chức lại điều này để giải quyết tình huống khó xử này mà tôi đang gặp phải không?

+1

Một tùy chọn dự phòng khác sẽ không có định nghĩa mặc định là 'g' và/hoặc' f', mà thay vào đó cung cấp chức năng độc lập mà người viết thể hiện có thể sử dụng triển khai "mặc định" (bằng cách viết 'g = defaultG'). Hy vọng rằng ít nhất làm cho nó một chút khó khăn hơn để * vô tình * để lại cả hai như mặc định, vì ít nhất một là một sự lựa chọn có chủ ý. – Ben

Trả lời

0

Câu trả lời trước của tôi là vô nghĩa, vì vậy thay vào đó, đây là một (hy vọng) tốt hơn. Điều này ít nhất sẽ cung cấp cho bạn một cảnh báo tại thời gian biên dịch. Thủ thuật là triển khai f' hoặc g trong lớp G nếu cả hai a có trường hợp cho cả FG, nhưng chỉ f nếu trường hợp của G là không thể.

{-# LANGUAGE DefaultSignatures #-} 

class C a where 

class F a where 
    f :: a -> a 
    default f :: (G a) => a -> a 
    f = g 

class F a => G a where 
    {-# MINIMAL (f'|g) #-} 

    f' :: a -> a 
    f' = f 

    g :: a -> a 
    default g :: (C a) => a -> a 
    g = f' 

instance F Integer where 
    f = succ 

instance F Int 
instance G Int where 
    g = succ 

instance C Float 
instance F Float 
instance G Float where 
    f' = succ 

-- This will give a compile time warning, but will still 
-- loop at runtime: 
instance C Double 
instance F Double 
instance G Double 
Các vấn đề liên quan