Cast implicito e statico (coercizione) in Haskell


9

Problema

Considera il seguente problema di progettazione in Haskell. Ho un semplice EDSL simbolico in cui voglio esprimere variabili ed espressioni generali (polinomi multivariati) come x^2 * y + 2*z + 1. Inoltre, voglio esprimere alcune equazioni simboliche sulle espressioni, per esempio x^2 + 1 = 1, così come le definizioni , come x := 2*y - 2.

L'obiettivo è:

  1. Hanno un tipo separato per variabili ed espressioni generali: alcune funzioni potrebbero essere applicate a variabili e non a espressioni complesse. Ad esempio, un operatore di definizione:= potrebbe essere di tipo (:=) :: Variable -> Expression -> Definitione non dovrebbe essere possibile passare un'espressione complessa come parametro sul lato sinistro (sebbene dovrebbe essere possibile passare una variabile come parametro sul lato destro, senza cast esplicito ) .
  2. Hanno espressioni in un'istanza di Num, in modo che sia possibile promuovere letterali interi in espressioni e usare una pratica notazione per operazioni algebriche comuni come addizione o moltiplicazione senza introdurre alcuni operatori wrapper ausiliari.

In altre parole, vorrei avere un cast di tipo implicito e statico (coercizione) di variabili alle espressioni. Ora, so che come tale, non ci sono cast di tipi impliciti in Haskell. Tuttavia, alcuni concetti di programmazione orientata agli oggetti (eredità semplice, in questo caso) sono espressi nel sistema di tipi di Haskell, con o senza estensioni del linguaggio. Come potrei soddisfare entrambi i punti sopra mantenendo una sintassi leggera? È anche possibile?

Discussione

È chiaro che il problema principale qui è Numla restrizione del tipo, ad es

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

In linea di principio, è possibile scrivere un singolo tipo di dati algebrico (generalizzato) sia per le variabili che per le espressioni. Quindi, si potrebbe scrivere :=in modo tale che l'espressione sul lato sinistro è discriminata e viene accettato solo un costruttore di variabili, con un errore di runtime altrimenti. Questa tuttavia non è una soluzione pulita, statica (cioè in fase di compilazione) ...

Esempio

Idealmente, vorrei ottenere una sintassi leggera come

computation = do
  x <- variable
  t <- variable

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

In particolare, desidero vietare la notazione come t + 1 |:=| x^2 - 1poiché :=dovrebbe dare una definizione di una variabile e non un'intera espressione sul lato sinistro.


1
forse potresti usare a class FromVar econ un metodo fromVar :: Variable -> ee fornire istanze per Expressione Variable, quindi le tue variabili hanno tipi polimorfici x :: FromVar e => eecc.
Mor A.

Non sono sicuro di come FromVarsarebbe utile la tabella dei tipi. Voglio evitare cast espliciti mantenendo Exprun'istanza di Num. Ho modificato la domanda aggiungendo un esempio di una notazione che vorrei ottenere.
Maciej Bendkowski il

Risposte:


8

Per sfruttare il polimorfismo piuttosto che il sottotipo (perché è tutto ciò che hai in Haskell), non pensare "una variabile è un'espressione", ma "sia le variabili che le espressioni hanno alcune operazioni in comune". Tali operazioni possono essere inserite in una classe di tipo:

class HasVar e where fromVar :: Variable -> e

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

Quindi, piuttosto che fondere le cose, rendere le cose polimorfiche. Se lo hai v :: forall e. HasVar e => e, può essere usato sia come espressione che come variabile.

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

 where

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

Scheletro per rendere il codice sotto typecheck: https://gist.github.com/Lysxia/da30abac357deb7981412f1faf0d2103

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

Interessante, grazie! Ho pensato di nascondere sia le variabili che le espressioni dietro i tipi esistenziali per un breve momento, tuttavia ho respinto l'idea perché ha introdotto una notazione aggiuntiva, vedi la tua V. Inizialmente non è quello che volevo, ma forse sono stato troppo veloce per scartarlo ... Probabilmente non riesco a liberarmi dell'opaco V. In una nota correlata, come posso creare un'istanza di V (forall e . HasVar e => e)? In Coq, userei i calcoli dei tipi e la corrispondenza dei modelli su un tipo induttivo, ma non è chiaro come raggiungerlo in Haskell.
Maciej Bendkowski il

1
È possibile prendere un w :: Variablequalche modo e applicare fromVarad esso: variable = (\w -> V (fromVar w)) <$> (_TODO_ :: Solver Variable).
Li-yao Xia

1
E Vpotrebbe essere evitato con tipi impredicativi, ma è comunque WIP. Oppure possiamo fare in modo variableche la continuazione con un argomento polimorfico sia esplicita anziché indirettamente tramite (>>=).
Li-yao Xia
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.