TL; DR: Ho bisogno di aiuto per capire come generare codice che restituirà uno di un piccolo numero di tipi di dati (probabilmente solo Double e Bool) da vari campi su record disparati.Come creare una DSL per cercare campi da un record in Haskell
forma lunga: Assumendo i seguenti tipi di dati
data Circle = Circle { radius :: Integer, origin :: Point }
data Square = Square { side :: Integer }
e un po 'di codice standard
circle = Circle 3 (Point 0 0)
square = Square 5
Sto costruendo un piccolo DSL, e vogliono l'utente da scrivere qualcosa di simile al seguente
circle.origin
square.side
e sarà Codice enerate simile a
origin . circle
side . square
In analisi di questo, avrei le stringhe "cerchio" e "origine", per esempio. Ora ho bisogno di trasformarli in chiamate di funzione. Potrei ovviamente avere qualcosa di simile:
data Expr a = IntegerE (a -> Integer)
| PointE (a -> Point)
lookupF2I "side" = Just $ IntegerE side
lookupF2I "radius" = Just $ IntegerE radius
lookupF2I _ = Nothing
lookupF2P "origin" = Just $ PointE origin
lookupF2P _ = Nothing
e avere una funzione di ricerca per tipo di dati restituiti. Avere una funzione per tipo di dati è pratica dal punto di vista DSL in quanto si occuperà solo di 2 o 3 tipi di dati. Tuttavia, questo sembra quasi un modo particolarmente efficace di fare le cose. C'è un modo migliore (sicuramente) di fare questo? In caso contrario, esiste un modo per generare il codice per le varie funzioni di ricerca dai vari record da cui si desidera poter cercare i campi?
secondo luogo, c'è ancora la questione del analizzato "circle"
o "square"
dover chiamare la funzione circle
o square
appropriata. Se dovessi implementare questo utilizzando le classi di tipo, avrei potuto fare qualcosa di simile:
instance Lookup Circle where
lookupF2I "radius" = Just $ IntegerE radius
lookupF2I _ = Nothing
lookupF2P "origin" = Just $ PointE origin
lookupF2P _ = Nothing
ma poi che mi lascia con la necessità di capire quale tipo di far rispettare sulla funzione di ricerca, e peggio di dover scrivere a mano istanze per ogni (di molti) record su cui voglio usare questo.
Nota: il fatto che Circle
e Square
possano essere rappresentati utilizzando un singolo ADT è secondario alla mia domanda in quanto si tratta di un esempio forzato. Il codice effettivo comporterà vari record molto diversi, di cui l'unica cosa che hanno in comune è avere campi dello stesso tipo.
Vorrei esaminare il pacchetto Lens: http://hackage.haskell.org/package/lens-3.1 e anche su Vinyl https://github.com/jonsterling/Vinyl – ErikR
+1 per 'lens'. Ha il supporto di Template Haskell per poter creare automaticamente obiettivi da tipi di dati. Quindi il tuo codice di analisi si limita a tradurre le stringhe negli obiettivi corrispondenti. –
Sembra che tu stia incorporando il tuo DSL con una sorta di HOAS (sintassi astratta dell'ordine superiore) che utilizza le funzioni del linguaggio meta (Haskell) nel linguaggio incorporato. Se vuoi alcune note sull'utilizzo di HOAS, guarda il documento "Unembedding" di Robert Atkey e co-autore https://personal.cis.strath.ac.uk/robert.atkey/unembedding.html. Se HOAS non significa nulla per te, allora probabilmente stai meglio con la normale sintassi del primo ordine: rappresentano funzioni incorporate con un identificatore e valutano con un ambiente che contiene una ricerca delle funzioni primitive. –