Frege (llenguatge de programació)

De Viquipèdia
Dreceres ràpides: navegació, cerca

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 | modifica el codi]

-- 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 | modifica el codi]

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'instaŀ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 | modifica el codi]

Un resum de les diferències amb Haskell està llistat a la pàgina Differences between Frege and Haskell.

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 | modifica el codi]

Enumeració d'arguments[modifica | modifica el codi]

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 | modifica el codi]

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 | modifica el codi]

  • 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ó com al Haskell.
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. (sequenciació de funcions amb efectes col·laterals)
 
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 | modifica el codi]

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'estat de la Mònada
data JRuntime s = native java.lang.Runtime  
  where
    -- mètode estàtic
    native jrtGetRuntime java.lang.Runtime.getRuntime :: () -> IO (JRuntime RealWorld)
    -- mètode de la instància (el primer paràmetre és del tipus definit)
    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'instaŀ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 | modifica el codi]

Referències[modifica | modifica el codi]

  1. Downloads Language reference.
  2. What is frege (or what will it be)? Butlletí històric.
  3. Frege project - Getting started
  4. PreludeBase.fr
  5. Monadic related functionality

Enllaços externs[modifica | modifica el codi]

A fons[modifica | modifica el codi]