Frege (programming language)

From Wikipedia, the free encyclopedia
Jump to: navigation, search

Frege is a non-strict, pure functional programming language in the spirit of Haskell. It enjoys a strong static type system with type inference. Higher rank types are supported, though type annotations are required for that.[1] The programming language is named after the German mathematician, logician and philosopher Gottlob Frege. While it is not a Haskell dialect, some effort has been put into making Frege look and feel like Haskell.[1]

Frege programs are compiled to Java bytecode and run in a Java virtual machine. Existing Java classes and methods can be used seamlessly from Frege.

Frege authoring is attributable to Ingo Wechsung.[2][3]

The Frege programming language is unrelated to the Frege Program Prover.

Contents

Comparison with Haskell [edit]

A summary of differences between Frege and Haskell is listed at the Differences between Frege and Haskell.

The type String is custom defined as an interface with Java strings. String (++) is bound to Java's String (+).[4] Conversion functions to Haskell correspondent:

packed :: [Char] -> String
unpacked :: String -> [Char]

Literals:

-- boolean literals true false are not capitalized
-- frege: data Ordering = Lt | Eq | Gt
-- haskell: data Ordering = LT | EQ | GT

Numeric literals are not overloaded. They follow the Java rules for numeric literals.

test = (*5) 5L  -- type error!!
 
-- with fromInt :: Num a => Int -> a
 
test = (* fromInt 5) 5L    -- passes typechecker
fregeFromToList = 1..5  -- without brackets :: [Int]
haskellFromToList = [1..5] -- :: (Num t, Enum t) => [t]

Frege's Monad class does not include the method fail, included in a separate class MonadFail.[5]

Frege does not implement Haskell-like exceptions. Exceptions in calls to the native interface are caught and returned in an (Either JException resultType).

Numeric classes for floating point types are also different. Haskell's classes Fractional, RealFrac, RealFloat and Floating are not defined. Haskell's class Real defines toRational while Frege's defines (/):[4]

class Real (Num r) => r where     -- classname precedes context
    --- the division operator
    (/) :: r -> r -> r

Hello World program [edit]

-- file hello.fr
module Hello where  -- moduleName maybe hierarchical as pkgdir.JavaClassname
 
main args = println $ "Hello world! your arguments are: " ++ show args

Compiling Frege programs [edit]

Frege requires Java-7 JDK to compile and run.

at the console [edit]

As the "Getting started" page states,[6] to compile it:

mkdir classes
java -Xss1m -jar <install-dir>/fregec.jar -d classes src/hello.fr

This assumes the downloaded frege3.xx.vvv.jar has been renamed to fregec.jar for ease of use.

To run the compiled program specify the package name as start class. On GNU/Linux and other Unix systems:

java -cp classes:<install-dir>/fregec.jar Hello arg1 arg2

Hello world! your arguments are: ["arg1", "arg2"]

On Microsoft Windows the classpath separator has to be changed to ';'

at the Eclipse devel. environment [edit]

There is a plug-in for Eclipse with instructions given at How-to EclipseFregIDE.

More involved examples [edit]

Arguments listing [edit]

module Test where
 
-- main :: [String] -> IO ()
main args = do
    println "Hello World!"
    case args of
      [] -> println "Usage: give some parameters"
      _ -> do
        println "here come your parameters:"
        let pairs = zip numbering args  
        forM_ pairs $ uncurry 
           -- anonymous function
           (\pos -> \arg -> do  -- (->) between params. can be omitted, but not the backslashes
                          print $ show pos ++ ". "  
                          printArg arg
           )
  where
    numbering = iterate (+1) 1  --  Haskell's [1..] doesn't parse -- :: [Int]
 
    -- function composition
    printArg = println • ("arg: "++)  -- prepend label and print

data type admits methods to match a JavaVM Class interface [edit]

Let's view some more advanced constructs while showing how to reuse code on record types with properties in common.

Field accessors have to be used with the dot notation and they don't imply a corresponding function (à la Haskell) in the module namespace. (you have to qualify them like RecordType.fieldName)

-- file fregeRec.fr
module FregeRec where 
-- with java scalar types incl. String
data TRec = RecConstructor { fld1 :: Long, fld2 :: String, fld3 :: Int}  
   where                                      -- some methods follow
     new cnt = RecConstructor 0L "abc" cnt     -- positional initialisation with java literals
     getProp1 (obj::TRec) = obj.fld1
     setProp1 (obj::TRec) v = obj.{fld1 = v}  -- dot before update clause
     incrProp1 (obj::TRec) = obj.{fld1 <- (+1L)}  -- apply function to field
 
derive Eq TRec ; derive Show TRec
 
-- using the record ''methods'' with navigational style
getATRecObj cnt = ((TRec.new cnt
                    ).setProp1 1L
                    ).incrProp1
 
-- the following line throws a compiler error!!: can't resolve `fld1` 
-- getField1 (obj::TRec) = fld1 obj  -- fld1 does not belong to the module namespace
 
data TRec2 = RecConstructor2 { fld4 :: Long, fld5 :: Float}  
   where                                      -- some methods follow
     new = RecConstructor2 { fld4 = 4L, fld5 = 0.0f}   -- initialisation byName with java literals
     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)
 
-- typeclassing record methods defined above 
--   to derive functionality over them
--   so we can use it with different record types having methods in common
 
class HasProp1Long t where
  getProp1 :: t -> Long
 
-- derived typeclass
 
class PrintProp1 (HasProp1Long t) => t where    -- classname precedes context requirements
  printProp1 :: t -> IO ()
  printProp1 rec = println $ "printProp1: " 
                       ++ showLong rec.getProp1  
    where 
      showLong (longv::Long) = show longv
 
instance PrintProp1 TRec
instance PrintProp1 TRec2
 
-- 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 / Exported identifiers [edit]

The IORef data type has Java style methods {.new iniVal, .get, .put val}.

Exported identifiers
There is no export list at the module clause. Instead, private / protected / public (default) access qualifiers. Protected items will only be available when explicitly mentioned in an import list.
Operator definition
to introduce a fresh symbol operator one must write it within backquotes in the fixity declaration. Associativity (left assoc.: infixl, right assoc.:infixr, no assoc:infix). Precedence range is 1..16 (unlike Haskell's 0..9) . See the op. precedence table at the manual.
module IORefTest where
-- Monad's ops. use 
-- At the right of '>>=' is a computation function (funct. with side effects)
--   on the result of the left side computation
 
infixr 3 `>>>`  -- operator fixity, within backquotes (unlike Haskell)
f >>> g = g • f   -- left-to-right (more readable) function composition (arrow style (>>>), F# style (>>))
 
-- instead of Haskell's export lists, ''private''/''protected'' qualifiers
 
private incrCounter :: Enum a => IORef a -> IO a
private incrCounter enumRef = do
 
              enumRef.get >>= (succ >>> enumRef.put)  
              enumRef.get 
 
infixl 1 `|>`   -- object navigational application (FSharp's pipeline op.)
x |> f = f x
 
main _ = do
  refCnt <- IORef.new 0        -- :: IO (IORef Int)
  cnt <- incrCounter refCnt
  cnt
      |> show
      |> ("counter shows: " ++)
      |> println

Accessing Java [edit]

See chapter "Native Interface" of the Manual.

  • Calls to native Java must be handled in a Monad. (Except for pure tagged calls).
  • Java nullable references as parameters translate into Maybe wrappers.
  • Java calls that can throw exceptions must have a result type wrapped in an Exception resultType which is synonym of (Either JException resultType).
// file src/pkgdir/StringExtra.java
package pkgdir ;
 
public class StringExtra {
  // creating a class with extra functionality
 
  public static int lastIndexOf( String str, char ch) {
    return str.lastIndexOf( ch) ; 
  }
}
-- file src/pkgdir/MyNativeInterface.fr
module pkgdir.MyNativeInterface where
 
-- native types must include a phantom parameter, related to the monad state
data JRuntime s = native java.lang.Runtime  
  where
    -- static method
    native jrtGetRuntime java.lang.Runtime.getRuntime :: () -> IO (JRuntime RealWorld)
    -- instance method (the type of the first param. is the one defined)
    native jrtFreeMemory freeMemory :: JRuntime RealWorld -> IO Long  
 
getFreeMemory :: () -> IO Long
getFreeMemory () = do
  runtime <- JRuntime.jrtGetRuntime ()
  runtime.jrtFreeMemory
 
-- when the native called routine throws Exceptions  
native getSysProp java.lang.System.getProperty :: String -> IO (Exception (Maybe String))
 
-- a pure method
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 "++ show string ++ " "++ show ch ++" is: "++ show pos
  where 
    pos = stringLastIndexOf string ch 
 
main _ = do
        mem <- getFreeMemory ()
        println $ "freeMem: " ++ show mem
        testSysProp "myprop"
        testStrLastIndexOf "abc/def/ghi" '/'

running:

# set FREGE_HOME to where it is installed
# cd to src parent dir.
$ mkdir classes

# compile Java files
$ javac -cp classes:$FREGE_HOME/fregec.jar -d classes src/pkgdir/StringExtra.java

# compile Frege files
$ java -Xss1m -jar $FREGE_HOME/fregec.jar -d classes src/pkgdir/MyNativeInterface.fr src/pkgdir/Test1.fr

# run
$ java -cp classes:$FREGE_HOME/fregec.jar -Dmyprop=mypropval pkgdir.Test

freeMem: 28196256
system prop. myprop is: mypropval
stringLastIndexOf "abc/def/ghi" '/' is: 7

References [edit]

  1. ^ a b The Frege project
  2. ^ Downloads Language reference.
  3. ^ What is frege (or what will it be)? Historical post.
  4. ^ a b PreludeBase.fr
  5. ^ Monadic related functionality
  6. ^ Frege project - Getting started

External links [edit]

In depth [edit]