Representasi rangkaian pada level type

8

Saya ingin belajar lebih banyak tentang pemrograman concatenative melalui pembuatan bahasa kecil yang sederhana, berdasarkan stack dan mengikuti paradigma concatenative.

Sayangnya, saya belum menemukan banyak sumber daya mengenai bahasa konkatatif dan implementasinya, jadi maafkan saya sebelumnya untuk kemungkinan kenaifan saya.

Karena itu saya mendefinisikan bahasa saya sebagai urutan sederhana rangkaian fungsi, yang diwakili dalam AST sebagai daftar:

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

data Literal
    = Int Int
    | Float Float

data LiteralOperation
    = Add | Sub | Mul | Div

Program berikut, 4 2 swap dup * +(sesuai dengan 2 * 2 + 4) setelah diuraikan, akan memberikan AST berikut:

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

Sekarang saya harus menyimpulkan dan memeriksa tipenya.

Saya menulis sistem jenis ini:

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

Di situlah pertanyaan saya masuk, karena saya tidak tahu tipe apa yang dapat disimpulkan dari ungkapan itu. Jenis yang dihasilkan jelas, Inttapi saya tidak tahu bagaimana sebenarnya memeriksa program ini pada tingkat jenis.

Pada awalnya, seperti yang dapat Anda lihat di atas, saya telah memikirkan TConcjenis yang mewakili penggabungan dengan cara yang sama seperti TFunjenis mewakili fungsi, karena pada akhirnya urutan rangkaian membentuk fungsi yang unik.

Pilihan lain, yang belum saya eksplorasi, adalah menerapkan aturan inferensi komposisi fungsi untuk setiap elemen dari urutan ekspresi ini. Saya tidak tahu bagaimana cara kerjanya dengan stack-based.

Pertanyaannya adalah: bagaimana kita melakukannya? Algoritme mana yang digunakan, dan pendekatan mana pada level tipe yang harus dipilih?

Licik
sumber

Jawaban:

9

Gagasan utama bahasa concatenative adalah bahwa sintaks dan domain semantik membentuk monoids dan semantik adalah homomorfisme monoid . Sintaksnya adalah monoid gratis yang dihasilkan oleh operasi dasar, lebih dikenal sebagai daftar. Operasi itu adalah daftar rangkuman, yaitu (++)di Haskell. Dalam konteks yang tidak diketik, domain semantik hanyalah monoid fungsi akhir (di tumpukan) dengan komposisi sebagai operasi. Dengan kata lain, seorang juru bahasa harus terlihat seperti berikut:

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 []

Membuat kompiler ( sangat naif) sama mudahnya. Satu-satunya hal yang berubah adalah target monoid yang sekarang akan menjadi monoid sintaksis yang dibangun dari sebuah fragmen sintaks bahasa target dengan demikian interpretOpakan menjadi compileOp. Sasaran monoid ini dapat berupa urutan pernyataan dengan operasi komposisi sekuensial, yaitu ;. Anda bisa menjadi jauh lebih canggih sekalipun .

Sistem tipe untuk bahasa konkatatif tidak begitu jelas, dan hampir tidak ada bahasa konkatatif diketik. Cat adalah contoh paling penting yang saya ketahui. Salah satu cara untuk mulai mendekatinya dan mengalami beberapa masalah yang muncul adalah dengan menanamkan bahasa concatenative di Haskell. Anda dengan cepat menemukan bahwa Anda tidak ingin add :: (Int, Int) -> Intkarena ini tidak akan menulis. Sebaliknya, Anda memilikinya add :: (Int, (Int, s)) -> (Int, s). Ini bekerja sangat baik untuk hal-hal sederhana. Ini juga merupakan jenis baris pria yang relatif jelas miskin. Salah satu rintangan pertama dan paling signifikan yang akan Anda hadapi adalah berurusan dengan kutipan. Masalahnya adalah bahwa hal itu [add]harus sesuai dengan sesuatu dengan tipe seperti s -> ((forall s'. (Int, (Int, s')) -> (Int, s')), s)yang membutuhkan tipe peringkat lebih tinggi dan instantiasi impredikatif. Kucing tampaknya memiliki keduanya. Ini tentu saja memiliki tipe peringkat yang lebih tinggi, dan itu akan menggantikan sebuah tipe politis untuk variabel tipe. Mungkin melakukan hal-hal dengan cara yang dapat dipahami tanpa kebobrokan. Menyelesaikan ini dengan embedding di Haskell mungkin bisa dilakukan menggunakan daftar tipe-tingkat, keluarga tipe (tertutup), dan kuantifikasi universal lokal. Pada titik ini, membuat sistem tipe khusus sepertinya lebih masuk akal.

Operasi dengan efek tumpukan yang tidak seragam juga cenderung bermasalah, tetapi, dalam kebanyakan kasus, masuk akal untuk menghilangkannya dan menyediakan cara alternatif untuk melakukan hal-hal yang menjamin tumpukan yang konsisten.

Derek Elkins meninggalkan SE
sumber