2016-09-08 35 views
8

Mục tiêu của tôi là để loại bỏ () từ các thuật ngữ, như thế này:Loại giảm vòng lặp vô hạn

(a, b)  -> (a, b) 
((), b)  -> b 
(a, ((), b)) -> (a, b) 
... 

Và đây là đoạn code:

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE UndecidableInstances #-} 

module Simplify where 

import Data.Type.Bool 
import Data.Type.Equality 

type family Simplify x where 
    Simplify (op() x) = Simplify x 
    Simplify (op x()) = Simplify x 
    Simplify (op x y) = If (x == Simplify x && y == Simplify y) 
          (op x y) 
          (Simplify (op (Simplify x) (Simplify y))) 
    Simplify x   = x 

Tuy nhiên, cố gắng nó ra:

:kind! Simplify (String, Int) 

... dẫn đến vòng lặp vô hạn trong trình kiểm tra loại. Tôi nghĩ rằng gia đình loại If nên được chăm sóc của các điều khoản irreducible, nhưng tôi rõ ràng là thiếu một cái gì đó. Nhưng cái gì?

+4

Có vẻ như bạn đang giả định rằng tính toán loại cấp cao nhất là lười biếng, vì thế mà các chi nhánh thứ hai của 'If' sẽ không được đánh giá trừ khi nó là cần thiết. Tôi nghĩ rằng giả định là không chính đáng. –

+6

Và bằng cách này: là đa hình trên 'op' có lẽ là sai. Ví dụ, 'Simplify (Either() Int)' có lẽ không nên giảm xuống 'Int'. –

+0

Hành vi nào sẽ xảy ra trên '(String,(), Int)'? Cả hai giải pháp được đề xuất cho đến nay đều giảm xuống mức 'Int'. Tôi thậm chí không biết nếu nó có thể nhận được '(String, Int)'. – gallais

Trả lời

10

Nhập đánh giá gia đình không phải là lười biếng, vì vậy If c t f sẽ đánh giá tất cả c, tf. (Thực tế, loại đánh giá gia đình không thực sự được xác định ngay bây giờ.) Vì vậy, không có gì lạ khi bạn kết thúc với một vòng lặp vô hạn - bạn luôn đánh giá Simplify (op (Simplify x) (Simplify y)), ngay cả khi đó là Simplify (op x y)!

Bạn có thể tránh điều này bằng cách phân chia các đệ quy và đơn giản hóa ngoài, như vậy:

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE UndecidableInstances #-} 

module Simplify where 

import Data.Type.Bool 
import Data.Type.Equality 

type family Simplify1 x where 
    Simplify1 (op() x) = x 
    Simplify1 (op x()) = x 
    Simplify1 (op x y) = op (Simplify1 x) (Simplify1 y) 
    Simplify1 x   = x 

type family SimplifyFix x x' where 
    SimplifyFix x x = x 
    SimplifyFix x x' = SimplifyFix x' (Simplify1 x') 

type Simplify x = SimplifyFix x (Simplify1 x) 

Ý tưởng là:

  1. Simplify1 làm một bước đơn giản hóa.
  2. SimplifyFix mất x và đơn giản hóa một bước x', kiểm tra xem chúng có bằng nhau không và nếu chúng không thực hiện một bước đơn giản hóa khác (do đó tìm điểm cố định là Simplify1).
  3. Simplify chỉ cần bắt đầu chuỗi SimplifyFix với cuộc gọi đến Simplify1.

Vì kiểu gia đình khớp mẫu là lười biếng, SimplifyFix đánh giá trễ chính xác, ngăn chặn vòng vô hạn.

Và quả thực:

*Simplify> :kind! Simplify (String, Int) 
Simplify (String, Int) :: * 
= (String, Int) 

*Simplify> :kind! Simplify (String, ((), (Int,()))) 
Simplify (String, ((), (Int,()))) :: * 
= ([Char], Int) 
+3

Chà, tôi cũng đang viết một câu trả lời ... và đã đưa ra * chính xác * lựa chọn tương tự cho trường hợp kiểm tra thứ hai.Đối với những gì nó có giá trị, tôi nghĩ rằng câu trả lời của tôi là hơi đơn giản, mặc dù có lẽ ít generalizable: 'Simplify1 ((), y) = y; Simplify1 (x,()) = x; Simplify1 other = other; Đơn giản hóa (x, y) = Simplify1 (Đơn giản hóa x, Đơn giản hóa y); Đơn giản hóa khác = khác'. –

+0

@ DanielWagner Ý tôi là, bạn phải làm tổ '()' s ít nhất là hai sâu cho một thử nghiệm thực sự, đúng không? :-) Và nó là tốt đẹp để so sánh giải pháp của bạn! –

+3

Ngoài ra, một chút ma thuật thú vị: ': tử tế! forall a. Simplify1 (a,()) = a'. Điều đó có một số thông minh thực sự, cho rằng nó phải thông báo hai mệnh đề đầu tiên của 'Simplify1' sẽ cho cùng một câu trả lời! –

3

tôi nghĩ rằng tôi muốn đề cập đến việc cho rằng đơn giản hóa có cấu trúc của một lần, không có nhu cầu để xây dựng giải pháp phức tạp này liên quan đến một fixpoint mà lại đi qua các biểu hiện lặp đi lặp lại.

này sẽ làm tốt:

{-# LANGUAGE TypeFamilies   #-} 
{-# LANGUAGE UndecidableInstances #-} 
module Simplify where 

type family Simplify x where 
    Simplify (op a b) = Op op (Simplify a) (Simplify b) 
    Simplify x  = x 

type family Op op a b where 
    Op op() b = b 
    Op op a() = a 
    Op op a b = op a b 
Các vấn đề liên quan