Frege (llenguatge de programació)
Tipus | llenguatge de programació purament funcional i llenguatge de programació |
---|---|
Epònim | Gottlob Frege |
Paradigma de programació | programació funcional i purely functional programming (en) |
Darrera versió estable | 3.23.288 () 3.24 () |
Influenciat per | Java i Haskell |
Sistema operatiu | multiplataforma |
Extensió dels fitxers | fr |
Codi font | Codi font |
Pàgina web | github.com… |
Frege és un llenguatge de programació funcional, pur, d'avaluació tardana, de la família del llenguatge Haskell i influenciat pel Java. Té un sistema de tipatge fort amb tipus estàtics i inferència de tipus.
Frege és creat per a l'ecosistema de la Màquina Virtual Java proporcionant facilitat d'interacció amb classes generades des d'altres llenguatges a la mateixa màquina virtual.
Frege és atribuïble a Ingo Wechsung (Language reference doc.).[1] Butlletí històric.[2]
Frege no és un dialecte de Haskell encara que hi té moltes concordances. Ve a ésser un assaig reeixit de Haskell, però més acadèmic que normatiu (per ex. la classe Mònada no incorpora el mètode fail, que s'inclou en una classe derivada MonadFail), incorporant al llenguatge trets amb paral·lelismes al model de la màquina virtual Java subjacent.
El nom del llenguatge de programació pretén honorar Gottlob Frege (un dels inventors de la currificació).
Programa Hola Món
[modifica]-- fitxer hola.fr
-- el mòdul equival a una ''classe Java'' (el nom pot ser jeràrquic com a pkgdir.NomDeLaClasseJava)
module Hola where
main args = println $ "Hola món! heus aquí els arguments: " ++ show args
Compilant programes Frege
[modifica]Frege requereix un Java Development Kit (JDK) de la versió Java-7.
Com s'esmenta a la pàgina "Getting started"[3]
mkdir classes java -Xss1m -jar <carpeta-d'instal·lació>/fregec.jar -d classes hola.fr
Aquí assumim que el compilador descarregat com a frege3.xx.vvv.jar ha estat reanomenat a fregec.jar.
Per executar el programa, cal esmentar-ne el nom de la classe d'arrencada.
$ java -cp classes:<carpeta-d'instal·lació>/fregec.jar Hola arg1 arg2 Hola món! heus aquí els arguments: ["arg1", "arg2"] runtime 0.125 wallclock seconds.
Sobre Microsoft Windows el separador de la classpath cal que sigui ';' en comptes de ':'
Diferències amb Haskell
[modifica]Un resum de les diferències amb Haskell està llistat a la pàgina Differences between Frege and Haskell[Enllaç no actiu].
El tipus String està definit com una interfície amb les cadenes Java. L'op. (++) està lligat al (+) de les cadenes Java.[4] Però l'String de Frege implementa una classe ListLike que defineix els mètodes essencials de les llistes (Vegeu el mòdul PreludeList). Funcions de conversió al tipus corresponent al Haskell [Char]:
packed :: [Char] -> String
unpacked :: String -> [Char]
Literals:
-- els literals cert i fals no han de començar per majúscula.
-- frege: data Ordering = Lt | Eq | Gt
-- haskell: data Ordering = LT | EQ | GT
Els literals numèrics no es poden emprar com al Haskell associats a una classe de tipus (sobrecàrrega d'operacions), sinó, com al Java, associats a un tipus.
test = (*5) 5L -- error de tipus
--, l'aplicació parcial (*5) té tipus (:: Int -> Int) i no és aplicable al literal Long 5L
-- amb fromInt :: Num a => Int -> a
test = (* fromInt 5) 5L -- ara sí que passa la comprovació de tipus
fregeFromToList = 1..5 -- sense les claus [] :: [Int]
haskellFromToList = [1..5] -- :: (Num t, Enum t) => [t]
La classe Monad del Frege no inclou el mètode fail, que s'inclou en una classe separada MonadFail.[5]
Frege no implementa les excepcions del Haskell. Les excepcions de les crides a Java són caçades per la interfície de crides al llenguatge nadiu que han d'incorporar el resultat en un tipus (Either JException tipusDelResultat).
Les classes numèriques per a coma flotant no es corresponen amb les de Haskell.
-- el nom de la classe definida precedeix els requeriments del context.
class Real (Num r) => r where
--- l'operador de divisió real, que al Haskell està definit a la classe ''Fractional''
(/) :: r -> r -> r
Exemples més elaborats
[modifica]Enumeració d'arguments
[modifica]module Test where
-- main :: [String] -> IO ()
main args = do
println "Hello World!"
case args of
[] -> println "Ús: afegiu-hi qualque paràmetre"
_ -> do
println "vet aquí els vostres paràmetres:"
let pairs = zip numeració args
forM_ pairs $ uncurry
-- funció anònima
(\pos -> \arg -> do -- barra invertida a cadascun dels encaixos
-- els (->) entre paràmetres es poden estalviar
print $ show pos ++ ". "
imprimeixArg arg
)
where
numeració = iterate (+1) 1 -- el [1..] del Haskell no està implementat -- :: [Int]
-- composició de funcions (prefixar etiqueta i imprimir)
imprimeixArg = println • ("arg: "++)
El tipus de dada Registre admet mètodes, per casar amb les classes de la JavaVM
[modifica]Els accessors per als camps fan servir la notació del punt (navegació de dades) i no comporten una funció a nivell de mòdul com fa el Haskell. (L'equivalent s'obté qualificant amb el Tipus: Tipus.nomDeCamp).
-- fitxer fregeRec.fr
module FregeRec where
-- amb tipus escalars del Java
data TRec = RecConstructor { fld1 :: Long, fld2 :: String, fld3 :: Int}
where -- amb mètodes a continuació
new cnt = RecConstructor 0L "abc" cnt -- inicialització posicional amb literals Java
getProp1 (obj::TRec) = obj.fld1
setProp1 (obj::TRec) v = obj.{fld1 = v} -- cal posar un punt davant la clàusula d'actualització
incrProp1 (obj::TRec) = obj.{fld1 <- (+1L)} -- actualització amb una funció (al Haskell no hi és)
derive Eq TRec; derive Show TRec
-- fent servir els mètodes del registre amb estil navigacional.
getATRecObj cnt = ((TRec.new cnt
).setProp1 1L
).incrProp1
-- la propera línia dispararía un error!!: can't resolve `fld1`
-- getField1 (obj::TRec) = fld1 obj -- fld1 no pertany a l'espai de noms del mòdul
data TRec2 = RecConstructor2 { fld4 :: Long, fld5 :: Float}
where -- alguns mètodes tot seguit
new = RecConstructor2 { fld4 = 4L, fld5 = 0.0f} -- inicialització per nom amb literals Java
getProp1 (obj::TRec2) = obj.fld4
derive Eq TRec2; derive Show TRec2
getATRec2Obj () = TRec2.new
-- file test.fr
module Test where
import FregeRec (TRec, getATRecObj, TRec2, getATRec2Obj)
-- definirem una classe de tipus (similar als "interface" del Java)
class HasProp1Long t where
getProp1 :: t -> Long
-- classe derivada
-- El nom de la classe definida precedeix el context !!
class PrintProp1 (HasProp1Long t) => t where
printProp1 :: t -> IO ()
printProp1 rec = println $ "printProp1: "
++ showLong rec.getProp1
where
showLong (longv::Long) = show longv
-- instanciem la classe derivada per als dos tipus definits.
instance PrintProp1 TRec
instance PrintProp1 TRec2
-- en instanciar PrintProp1 intenta també instanciar la classe base HasProp1Long
-- però aquesta defineix una funció ja implementada als mètodes dels tipus
-- i el compilador ho troba tot bé.
-- main :: [String] -> IO ()
main args = do
let myTRecObj = FregeRec.getATRecObj 1
myTRec2Obj = FregeRec.getATRec2Obj ()
println $ "myTRecObj: " ++ show myTRecObj
println $ "myTRec2Obj: " ++ show myTRec2Obj
printProp1 myTRecObj
printProp1 myTRec2Obj
IORefs / Exportació d'identificadors
[modifica]- El tipus IORef té mètodes com el Java {.new iniVal, .get, .put val}.
- Identificadors exportats: L'exportació o no d'un símbol per part d'un mòdul no va com al Haskell sino mitjançant qualificadors d'accés private / protected / public (per omissió). Els etiquetats protected només s'exporten si el mòdul que els importa els menciona explícitament en una llista d'importació.
- la definició d'operadors requereix cometes revesses. El rang de precedència (1..16) és diferent del del Haskell (0..9).
- les crides a funcions sense paràmetres amb efectes col·laterals han de dur el paràmetre () (Unit) per evitar la memoïtzació.
module IORefTest where
-- definim un operador de composició d'esquerra a dreta (més fàcil de llegir)
infixr 3 `>>>` -- l'operador definit ha d'anar entre cometes revesses (diferència amb el Haskell)
f >>> g = g • f
-- l'operador '>>=' té el mateix significat que al Haskell. (encadenament monàdic)
private incrCounter :: Enum a => IORef a -> IO a
private incrCounter enumRef = do
enumRef.get >>= (succ >>> enumRef.put)
enumRef.get
main _ = do
refCnt <- IORef.new 0 -- :: IO (IORef Int)
cnt <- incrCounter refCnt
println $ "counter shows: " ++ show cnt
Interfície amb Java
[modifica]Cal afegir el qualificatiu native. Vegeu capítol "Native Interface" al Manual.
- Els mètodes purs (quin resultat només depèn dels paràmetres) han de dur el qualif. pure.
- Les definicions de tipus per a la interfície de classes Java amb estat han de dur un paràmetre fantasma (els que no figuren en la definició) per a l'estat corresponent al paràmetre de la Mònada (com ara (IO s)).
- Els paràmetres amb valors que admeten null cal embolcallar-los en un tipus Maybe.
- Els resultats de crides a Java que generen excepcions, cal embolcallar-los en un tipus Exception com ara Exception resultType sinònim de (Either JException resultType).
// file src/pkgdir/StringExtra.java
package pkgdir ;
public class StringExtra {
// creant una classe per afegir funcionalitat
public static int lastIndexOf(String str, char ch) {
return str.lastIndexOf(ch);
}
}
-- file src/pkgdir/MyNativeInterface.fr
module pkgdir.MyNativeInterface where
-- els tipus nadius han d'incorporar un paràmetre fantasma, corresponen a l'espai d'avaluació de la Mònada
-- quin paràm. actual serà RealWorld cas de la mònada IO, o bé en cas de la mònada (ST s) la mateixa var. com al Haskell
data JRuntime s = native java.lang.Runtime
where
-- FFI dels mètodes: "native" nom_Frege nom_Java :: tipus
native jrtGetRuntime java.lang.Runtime.getRuntime :: () -> IO (JRuntime RealWorld) -- mètode estàtic
-- mètode és de la instància sempre que el primer paràmetre sigui del tipus definit, altrament estàtic
native jrtFreeMemory freeMemory :: JRuntime RealWorld -> IO Long
getFreeMemory :: () -> IO Long
getFreeMemory () = do
runtime <- JRuntime.jrtGetRuntime ()
runtime.jrtFreeMemory
-- quan la rutina nadiua llança excepcions
native getSysProp java.lang.System.getProperty :: String -> IO (Exception (Maybe String))
-- un mètode pur (quin resultat només depèn dels paràmetres)
pure native stringLastIndexOf StringExtra.lastIndexOf :: String -> Char -> Int
-- file src/pkgdir/Test1.fr
module pkgdir.Test where
import pkgdir.MyNativeInterface
-- testSysProp :: String -> IO ()
testSysProp name = do
eitherRes <- getSysProp name -- :: IO (Exception (Maybe String))
case eitherRes of
Left excep -> println $ "exception: " ++ excep.getMessage
Right maybeResult ->
case maybeResult of
Just strRes -> println $ "system prop. "++ name ++ " is: " ++ strRes
Nothing -> println $ name ++ " system prop. is not defined"
-- testStrLastIndexOf :: String -> Char -> IO ()
testStrLastIndexOf string ch =
println $ "stringLastIndexOf "++string++" "++ charToString ch ++" is: "++ show pos
where
pos = stringLastIndexOf string ch
charToString ch = packed [ch]
main _ = do
mem <- getFreeMemory ()
println $ "freeMem: " ++ show mem
testSysProp "myprop"
testStrLastIndexOf "abc/def/ghi" '/'
executant:
# establir FREGE_HOME al directori d'instal·lació # cd al directori pare del ''src''. $ mkdir classes # compilant els fonts Java $ javac -cp classes:$FREGE_HOME/fregec.jar -d classes src/pkgdir/StringExtra.java # compilant els fonts Frege $ java -Xss1m -jar $FREGE_HOME/fregec.jar -d classes src/pkgdir/MyNativeInterface.fr src/pkgdir/Test1.fr # execució: $ java -cp classes:$FREGE_HOME/fregec.jar -Dmyprop=mypropval pkgdir.Test freeMem: 28196256 system prop. myprop is: mypropval stringLastIndexOf abc/def/ghi / is: 7
Relacionat
[modifica]Referències
[modifica]- ↑ Downloads[Enllaç no actiu] Language reference.
- ↑ What is frege (or what will it be)? Butlletí històric.
- ↑ Frege project - Getting started[Enllaç no actiu]
- ↑ «PreludeBase.fr». Arxivat de l'original el 2014-08-22. [Consulta: 21 maig 2012].
- ↑ «Monadic related functionality». Arxivat de l'original el 2015-05-12. [Consulta: 21 maig 2012].
Enllaços externs
[modifica]A fons
[modifica]- Referència del llenguatge (descàrregues)[Enllaç no actiu]
- Exemples del Frege Arxivat 2015-05-12 a Wayback Machine.