Pemain tipe implisit, statis (paksaan) di Haskell

9

Masalah

Pertimbangkan masalah desain berikut ini di Haskell. Saya memiliki, EDSL simbolis sederhana di mana saya ingin mengekspresikan variabel dan ekspresi umum (polinomial multivariat) seperti x^2 * y + 2*z + 1. Selain itu, saya ingin mengekspresikan persamaan simbolik tertentu atas ekspresi, katakanlah x^2 + 1 = 1, serta definisi , seperti x := 2*y - 2.

Tujuannya adalah untuk:

  1. Memiliki tipe terpisah untuk variabel dan ekspresi umum - fungsi tertentu mungkin diterapkan pada variabel dan bukan ekspresi kompleks. Misalnya, operator definisi:= mungkin bertipe (:=) :: Variable -> Expression -> Definitiondan seharusnya tidak mungkin untuk melewatkan ekspresi kompleks sebagai parameter sisi kiri (meskipun harus mungkin untuk melewatkan variabel sebagai parameter sisi kanannya, tanpa casting eksplisit ) .
  2. Buatlah ekspresi dari instance Num, sehingga dimungkinkan untuk mempromosikan literal integer ke ekspresi dan menggunakan notasi yang nyaman untuk operasi aljabar umum seperti penambahan atau perkalian tanpa memperkenalkan beberapa operator pembungkus tambahan.

Dengan kata lain, saya ingin memiliki tipe cast (paksaan) variabel implisit dan statis untuk ekspresi. Sekarang, saya tahu bahwa dengan demikian, tidak ada gips tipe implisit di Haskell. Namun demikian, konsep pemrograman berorientasi objek tertentu (pewarisan sederhana, dalam hal ini) dapat diekspresikan dalam sistem tipe Haskell, baik dengan atau tanpa ekstensi bahasa. Bagaimana saya bisa memenuhi kedua poin di atas sambil mempertahankan sintaksis yang ringan? Apakah itu mungkin?

Diskusi

Jelas bahwa masalah utama di sini adalah Numpembatasan jenis, misalnya

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

Pada prinsipnya, dimungkinkan untuk menulis tipe data aljabar tunggal (umum) untuk variabel dan ekspresi. Kemudian, orang dapat menulis :=sedemikian rupa, sehingga ungkapan sisi kiri didiskriminasi dan hanya konstruktor variabel yang diterima, dengan kesalahan run-time sebaliknya. Namun itu bukan solusi yang bersih, statis (mis. Waktu kompilasi) ...

Contoh

Idealnya, saya ingin mencapai sintaks yang ringan seperti

computation = do
  x <- variable
  t <- variable

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

Secara khusus, saya ingin melarang notasi seperti t + 1 |:=| x^2 - 1karena :=harus memberikan definisi variabel dan bukan seluruh ekspresi sisi kiri.

Maciej Bendkowski
sumber
1
mungkin Anda bisa menggunakan class FromVar edengan metode fromVar :: Variable -> edan memberikan contoh untuk Expressiondan Variable, kemudian variabel Anda memiliki tipe polimorfik x :: FromVar e => edll. Saya belum menguji seberapa baik ini bekerja karena saya di ponsel saya sekarang.
Mor A.
Saya tidak yakin bagaimana FromVartypeclass akan membantu. Saya ingin menghindari gips secara eksplisit sambil menjaga Exprinstance Num. Saya mengedit pertanyaan dengan menambahkan contoh notasi yang ingin saya capai.
Maciej Bendkowski

Jawaban:

8

Untuk meningkatkan polimorfisme daripada subtyping (karena itu yang Anda miliki di Haskell), jangan berpikir "variabel adalah ekspresi", tetapi "kedua variabel dan ekspresi memiliki beberapa operasi yang sama". Operasi-operasi itu dapat dimasukkan ke dalam kelas tipe:

class HasVar e where fromVar :: Variable -> e

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

Kemudian, alih-alih melempar sesuatu, buatlah sesuatu yang polimorfik. Jika sudah v :: forall e. HasVar e => e, itu bisa digunakan baik sebagai ekspresi maupun sebagai variabel.

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

 where

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

Skeleton untuk membuat kode di bawah ini ketiklah: https://gist.github.com/Lysxia/da30abac357deb7981412f1faf0d2103

computation :: Solver ()
computation = do
  V x <- variable
  V t <- variable
  t |:=| x^2 - 1
  solve (t |==| 0)
Li-yao Xia
sumber
Menarik, terima kasih! Saya menganggap menyembunyikan variabel dan ekspresi di balik tipe eksistensial untuk sesaat, namun saya menolak gagasan itu karena memperkenalkan notasi tambahan, lihat V. Awalnya bukan itu yang saya inginkan, tapi mungkin saya terlalu cepat untuk mengabaikannya ... Mungkin saya tidak bisa menghilangkan buram V. Pada catatan terkait, bagaimana saya bisa membuat instance dari V (forall e . HasVar e => e)? Dalam Coq, saya akan menggunakan tipe komputasi dan pencocokan pola pada tipe induktif, tetapi tidak jelas bagaimana mencapainya di Haskell.
Maciej Bendkowski
1
Anda bisa ambil w :: Variableentah bagaimana dan menerapkan fromVaruntuk itu: variable = (\w -> V (fromVar w)) <$> (_TODO_ :: Solver Variable).
Li-yao Xia
1
Dan Vbisa dihindari dengan tipe impredikatif, tapi itu masih WIP. Atau kita bisa variablemengambil kelanjutan dengan argumen polimorfik secara eksplisit, bukan secara tidak langsung melalui (>>=).
Li-yao Xia