Đại diện của sự kết hợp ở cấp độ loại


8

Tôi muốn tìm hiểu thêm về lập trình ghép nối thông qua việc tạo ra một ngôn ngữ đơn giản nhỏ, dựa trên ngăn xếp và tuân theo mô hình ghép.

Thật không may, tôi đã không tìm thấy nhiều tài nguyên liên quan đến các ngôn ngữ kết hợp và việc triển khai chúng, vì vậy xin lỗi trước cho sự ngây thơ có thể của tôi.

Do đó, tôi đã định nghĩa ngôn ngữ của mình là một chuỗi đơn giản của các hàm, được biểu diễn trong AST dưới dạng một danh sách:

data Operation
    = Concat [Operation]
    | Quotation Operation
    | Var String
    | Lit Literal
    | LitOp LiteralOperation

data Literal
    = Int Int
    | Float Float

data LiteralOperation
    = Add | Sub | Mul | Div

Chương trình sau, 4 2 swap dup * +(tương ứng với 2 * 2 + 4) một khi được phân tích cú pháp, sẽ cho AST sau:

Concat [Lit (Int 4), Lit (Int 2), Var "swap", Var "dup", LitOp Mul, LitOp Add]

Bây giờ tôi phải suy luận và kiểm tra các loại.

Tôi đã viết hệ thống loại này:

data Type
    = TBasic BasicType   -- 'Int' or 'Float'
    | TVar String        -- Variable type
    | TQuoteE String     -- Empty stack, noted 'A'
    | TQuote String Type -- Non empty stack, noted 'A t'
    | TConc Type Type    -- A type for the concatenation
    | TFun Type Type     -- The type of functions

Đó là nơi câu hỏi của tôi xuất hiện, bởi vì tôi không biết nên suy luận kiểu gì từ biểu hiện đó. Loại kết quả là rõ ràng, Intnhưng tôi không biết làm thế nào để thực sự kiểm tra hoàn toàn chương trình này ở cấp độ loại.

Lúc đầu, như bạn có thể thấy ở trên, tôi đã nghĩ về một TConcloại đại diện cho sự ghép nối giống như TFunkiểu đại diện cho một hàm, bởi vì cuối cùng, chuỗi nối tạo thành một hàm duy nhất.

Một tùy chọn khác, mà tôi chưa khám phá, sẽ là áp dụng quy tắc suy luận thành phần hàm cho từng phần tử của chuỗi biểu thức này. Tôi không biết làm thế nào nó sẽ hoạt động với dựa trên ngăn xếp.

Câu hỏi là như vậy: làm thế nào để chúng ta làm điều đó? Nên sử dụng thuật toán nào và cách tiếp cận nào ở cấp độ loại?

Câu trả lời:


9

Một ý tưởng chính của các ngôn ngữ ghép là cú pháp và miền ngữ nghĩa đơn hình và ngữ nghĩa là một cấu trúc đồng hình đơn hình . Cú pháp là monoid miễn phí được tạo bởi các hoạt động cơ bản, được gọi là danh sách. Hoạt động của nó là nối danh sách, tức là (++)trong Haskell. Trong bối cảnh chưa được xử lý, miền ngữ nghĩa chỉ là đơn thức của endofifts (trên ngăn xếp) với thành phần là hoạt động. Nói cách khác, một thông dịch viên sẽ trông giống như sau:

data Op = PushInt Int| Call Name | Quote Code | Add | ... -- etc.
type Code = [Op]

-- Run-time values
data Value = Q (Endo Stack) | I Int | ... -- etc.
type Stack = [Value]

-- You'd probably add an environment of type Map Name (Endo Stack)
interpretOp :: Op -> Endo Stack
interpretOp (PushInt n) = Endo (I n:)
interpretOp (Quote c) = Endo (Q (interpetCode c):)
interpretOp op = ... -- etc.

interpretCode :: Code -> Endo Stack
interpretCode = foldMap interpretOp

runCode :: Code -> Stack
runCode code = case interpretCode code of Endo f -> f []

Làm một trình biên dịch ( rất ngây thơ) chỉ đơn giản như vậy. Điều duy nhất thay đổi là monoid đích mà bây giờ sẽ là một monoid cú pháp được xây dựng từ một đoạn cú pháp của ngôn ngữ đích do đó interpretOpsẽ trở thành compileOp. Monoid mục tiêu này có thể là chuỗi các câu lệnh với hoạt động của thành phần tuần tự, nghĩa là ;. Bạn có thể khá phức tạp hơn rất nhiều mặc dù .

Loại hệ thống cho các ngôn ngữ ghép nối là không rõ ràng và hầu như không có ngôn ngữ nối ghép được gõ. Mèo là ví dụ quan trọng nhất mà tôi biết. Một cách để bắt đầu tiếp cận nó và trải nghiệm một số vấn đề nảy sinh là nhúng một ngôn ngữ nối trong Haskell. Bạn nhanh chóng phát hiện ra rằng bạn không muốn add :: (Int, Int) -> Intvì điều này sẽ không sáng tác. Thay vào đó, bạn có add :: (Int, (Int, s)) -> (Int, s). Điều này làm việc cực kỳ tốt cho những điều đơn giản. Đây cũng là loại hàng người nghèo tương đối rõ ràng. Một trong những rào cản đầu tiên và quan trọng nhất mà bạn gặp phải là xử lý các trích dẫn. Vấn đề là điều đó [add]phải tương ứng với một cái gì đó với loại s -> ((forall s'. (Int, (Int, s')) -> (Int, s')), s)yêu cầu loại cao cấp hơn và khởi tạo tạm thời. Mèo dường như có cả hai. Nó chắc chắn có các loại được xếp hạng cao hơn, và nó sẽ thay thế một polytype cho một biến loại. Nó có thể được thực hiện mọi thứ theo cách có thể được hiểu mà không cần thiết. Hoàn thành điều này với việc nhúng vào Haskell có thể được thực hiện bằng cách sử dụng danh sách cấp độ loại, họ kiểu đóng (và đóng) và định lượng phổ quát cục bộ. Tại thời điểm này, làm cho một hệ thống loại tùy chỉnh có thể có ý nghĩa hơn.

Các hoạt động với hiệu ứng ngăn xếp không đồng đều cũng có thể có vấn đề, nhưng, trong hầu hết các trường hợp, sẽ rất hợp lý nếu chỉ bỏ qua chúng và cung cấp các phương tiện khác để thực hiện những việc đảm bảo một ngăn xếp nhất quán.

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.