Ngẫu nhiên, kiểu đúc tĩnh (ép buộc) trong Haskell


9

Vấn đề

Hãy xem xét các vấn đề thiết kế sau đây trong Haskell. Tôi có một EDSL đơn giản, tượng trưng trong đó tôi muốn biểu thị các biến và biểu thức tổng quát (đa thức đa biến) như x^2 * y + 2*z + 1. Ngoài ra, tôi muốn diễn đạt các phương trình biểu tượng nhất định trên các biểu thức, nói x^2 + 1 = 1, cũng như các định nghĩa , như thế nào x := 2*y - 2.

Mục tiêu là để:

  1. Có một loại riêng cho các biến và biểu thức chung - một số hàm nhất định có thể được áp dụng cho các biến và không phải là biểu thức phức tạp. Ví dụ, một định nghĩa toán tử :=có thể là kiểu (:=) :: Variable -> Expression -> Definitionvà nó không phải là có thể vượt qua một biểu thức phức tạp như thông số bên trái tay của mình (mặc dù nó nên có thể để vượt qua một biến như tham số bên tay phải của mình, mà không cần đúc rõ ràng ) .
  2. Có các biểu thức là một ví dụ Num, để có thể quảng bá các số nguyên cho các biểu thức và sử dụng một ký hiệu thuận tiện cho các hoạt động đại số thông thường như cộng hoặc nhân mà không cần đưa ra một số toán tử trình bao bọc phụ trợ.

Nói cách khác, tôi muốn có một kiểu truyền ẩntĩnh (ép buộc) các biến thành biểu thức. Bây giờ, tôi biết rằng như vậy, không có phôi kiểu ẩn trong Haskell. Tuy nhiên, một số khái niệm lập trình hướng đối tượng nhất định (kế thừa đơn giản, trong trường hợp này) có thể biểu thị được trong hệ thống loại của Haskell, có hoặc không có phần mở rộng ngôn ngữ. Làm thế nào tôi có thể đáp ứng cả hai điểm trên trong khi vẫn giữ một cú pháp nhẹ? Nó thậm chí có thể?

Thảo luận

Rõ ràng vấn đề chính ở đây là Numhạn chế loại, vd

(+) :: Num a => a -> a -> a

Về nguyên tắc, có thể viết một kiểu dữ liệu đại số (tổng quát) cho cả hai biến và biểu thức. Sau đó, người ta có thể viết :=theo cách như vậy, biểu thức phía bên trái bị phân biệt đối xử và chỉ có một hàm tạo biến được chấp nhận, với lỗi thời gian chạy khác. Tuy nhiên, đó không phải là một giải pháp sạch, tĩnh (tức là thời gian biên dịch) ...

Thí dụ

Lý tưởng nhất, tôi muốn đạt được một cú pháp nhẹ như

computation = do
  x <- variable
  t <- variable

  t |:=| x^2 - 1
  solve (t |==| 0)

Cụ thể, tôi muốn cấm ký hiệu như t + 1 |:=| x^2 - 1từ đó :=nên đưa ra định nghĩa về một biến và không phải là toàn bộ biểu thức phía bên trái.


1
có lẽ bạn có thể sử dụng một class FromVar ephương thức fromVar :: Variable -> evà cung cấp các thể hiện cho ExpressionVariablesau đó các biến của bạn có các kiểu đa hình, x :: FromVar e => ev.v. Tôi chưa kiểm tra xem nó hoạt động tốt như thế nào kể từ khi tôi sử dụng điện thoại của mình.
Mor A.

Tôi không chắc chắn làm thế nào các FromVartypeclass sẽ được giúp đỡ. Tôi muốn tránh các diễn viên rõ ràng trong khi giữ Exprmột ví dụ Num. Tôi đã chỉnh sửa câu hỏi thêm một ví dụ về ký hiệu mà tôi muốn đạt được.
Maciej Bendkowski

Câu trả lời:


8

Để tận dụng tính đa hình thay vì phân nhóm (vì đó là tất cả những gì bạn có trong Haskell), đừng nghĩ rằng "một biến là một biểu thức", nhưng "cả hai biến và biểu thức đều có một số hoạt động chung". Những hoạt động có thể được đặt trong một lớp loại:

class HasVar e where fromVar :: Variable -> e

instance HasVar Variable where fromVar = id
instance HasVar Expression where ...

Sau đó, thay vì đúc mọi thứ, làm cho mọi thứ đa hình. Nếu bạn có v :: forall e. HasVar e => e, nó có thể được sử dụng cả dưới dạng biểu thức và biến.

example :: (forall e. HasVar e => e) -> Definition
example v = (v := v)  -- v can be used as both Variable and Expression

 where

  (:=) :: Variable -> Expression -> Definition

Bộ xương để tạo mã dưới đây typecheck: https://gist.github.com/Lysxia/da30abac357deb7981412f1faf0d2103

computation :: Solver ()
computation = do
  V x <- variable
  V t <- variable
  t |:=| x^2 - 1
  solve (t |==| 0)

Thật thú vị, cảm ơn! Tôi đã cân nhắc việc ẩn cả hai biến và biểu thức đằng sau các kiểu tồn tại trong một thời gian ngắn, tuy nhiên tôi đã từ chối ý tưởng này vì nó đã đưa ra ký hiệu bổ sung, xem của bạn V. Ban đầu đó không phải là điều tôi muốn, nhưng có lẽ tôi đã quá nhanh để loại bỏ nó ... Có lẽ tôi không thể thoát khỏi sự mờ đục V. Trên một lưu ý liên quan, làm thế nào tôi có thể tạo một ví dụ V (forall e . HasVar e => e)? Trong Coq, tôi sẽ sử dụng tính toán loại và khớp mẫu trên loại quy nạp, nhưng không rõ làm thế nào để đạt được điều đó trong Haskell.
Maciej Bendkowski

1
Bạn có thể lấy một cái gì w :: Variableđó và áp dụng fromVarcho nó : variable = (\w -> V (fromVar w)) <$> (_TODO_ :: Solver Variable).
Li-yao Xia

1
Vcó thể tránh được với các kiểu bắt buộc, nhưng đó vẫn là WIP. Hoặc chúng ta có thể thực variablehiện tiếp tục với một đối số đa hình rõ ràng thay vì gián tiếp thông qua (>>=).
Li-yao Xia
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.