Skip to content

LYAH adaptions for Frege

Ingo Wechsung edited this page Mar 7, 2015 · 28 revisions

This document is for those who want to learn Frege by going through Learn you a Haskell while doing the exercises in Frege. It will help you to deal with the small differences between both languages and the tools used.

Whenever there is an example, explanation or exercise that doesn’t work the same way in Frege as described in LYAH, you will find some remarks here. Chapter and section titles will be included for better orientation, as well as a short quote to establish the context for our comments. This is so that if you stumble upon some difficulty, say, in Chapter 2 “Starting out” and Section “Texas ranges”, you would easily find whether there is something here under those headings.

Nobody is perfect, and so are the authors of this wiki page. If you miss something, find something inaccurate or not detailed enough, please let us know by opening an issue in the Frege project.

This wiki page is done in admiration of the LYAH author, Miran Lipovača, who licensed his work with the Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. This allows us to build upon his material, provided that the same license applies to the derived work, which we herewith declare to be the case.

Why are there differences anyway?

The objective in the development of Frege was that it should be a practical language on the Java platform while being as close to Haskell as possible. This means in particular, that interoperability with Java and other JVM languages should be fluent.

Adaption to the JVM platform is the key reason for differences concerning basic types (Bool, String), low-level system functions, the module system and the foreign function interface, which is specialized for the JVM.

But now let’s jump right into LYAH!

Chapter 1. Introduction

What you need to dive in

Instead of ghci we use a similar tool which is known as Frege REPL. The only prerequisite for this is a Java7 (or higher) installation. You can check your java version like this:

$ java -version

If you don't have java installed, download the latest Java 8 JDK from Oracle. Or if you prefer, you can install the openjdk-8-jdk for your OS. For example, here is what would work on my box:

$ sudo apt-get install openjdk-8-jdk

Here is how you install and run the Frege REPL:

  • Download the latest distribution from the REPL download page.

  • Unzip the archive. This is done best in some directory dedicated solely to the REPL. After unzipping, it should look like this (apart from differences in the version numbers):

    $ cd; mkdir repl
    $ unzip ~/Downloads/frege-repl-1.1.1-SNAPSHOT.zip 
    Archive:  ~/Downloads/frege-repl-1.1.1-SNAPSHOT.zip
    [ ... snipped ... ]
    $ tree
    .
    ├── frege-repl-1.1.1-SNAPSHOT.jar
    └── lib
        ├── ecj-4.2.2.jar
        ├── frege-3.22.367-g2737683.jar
        ├── frege-interpreter-core-1.0.3-SNAPSHOT.jar
        ├── frege-interpreter-java-support-1.0.3-SNAPSHOT.jar
        ├── frege-repl-nativedeps-1.1.1-SNAPSHOT.jar
        └── jline-2.12.jar

The command to run this no matter what your current working directory is, would be:

$ java -Xss4m -Xmx1g -jar ~/repl/frege-repl-1.1.1-SNAPSHOT.jar -terminal jline

It is recommended to make this a shell alias, like

$ alias frepl='java -Xss4m -Xmx1g -jar ~/repl/frege-repl-1.1.1-SNAPSHOT.jar -terminal jline'

This way you just type frepl and that's it. The -terminal jline adds command history and command editing capabilities. This may not work on Windows. OTOH, the Windows cmd.exe can be configured to do the same, so you can remove the -terminal jline.

The Fast Way: The Online REPL

An even faster way to dive in may be the online version of the REPL. It's just one click away, and will be fine for the first exercises!

Chapter 2: Starting Out

Ready, set, go!

Congratulations, you're in GHCI!

Not surprisingly, the greeting of the Frege REPL is a bit different:

$ frepl   # you did make that alias, didn't you?
Welcome to Frege 3.22.367-g2737683 (Oracle Corporation OpenJDK Server VM, 1.8.0_40-internal)
frege> 2+2
4

The prompt is frege> for the time being, and cannot be changed. To get help, enter :help. Note that after starting the program, it will need a few seconds to answer even simple questions as 2+2. The reason is that the JVM needs to warm up a bit. Later on, the delays will get shorter.


If we want to have a negative number, it's always best to surround it with parentheses.

Indeed. Though, Frege doesn't have problems with

frege> 5 * -3
-15
frege> -5 * 3
-15
frege> -5 * -3
15

but this will only work in infix expressions. So, when in doubt, surround negative numbers with parentheses.

One more thing: you must set apart operators by at least one space character, lest the following happens:

frege> 5*-3
[ERROR: 4]: E <console>.fr:4: can't resolve `*-`, did you mean `*` perhaps?

Boolean algebra is also pretty straightforward.

frege> True || False
[ERROR: 4]: E <console>.fr:4: can't resolve `True`, did you mean `Byte` perhaps?
[ERROR: 4]: E <console>.fr:4: can't resolve `False`, did you mean `File` perhaps?

In Frege, we must always write true and false instead of True and False. This is because we use the primitive boolean type of the JVM, which appears as an abstract type Bool in Frege. Consequently, we also have the boolean literals true and false.

Confusing? Don't panic! A deeper explanation follows later, when we learn about algebraic data types. Please just mark this for now: write true and false, not True and False.

frege> true || false
true

5 /= 5

We can as well write != in Frege, but /= still works for Haskell compatibility. (To my knowledge, no other language uses /= for not equal.)


"hello" == "hello"

Remark for Java programmers: this actually compares the values, not the references, as Java would do. That is, this is equivalent to the Java expression:

"hello".equals("hello")

No instance for (Num [Char])

Frege will instead tell you:

frege> 5 + "llama"
[ERROR: 4]: E <console>.fr:4: String is not an instance of Num

In general, the error message will always come out different. No wonder, we have two different compilers, written by different people at different times.

Baby's first functions (subject to review by Marimuthu)

Once inside GHCI, do :l baby.

In Frege, you must give the file name with extension. So, if you saved as baby.hs, type :l baby.hs. However, Frege source code files should have file extension .fr.

Later on, when you load again the same file, you must give the :reset command before to avoid strange warnings, or even errors.

If you're using the Online REPL, you won't be able to access files on your computer. However, you can load scripts from a URL! If you have a GitHub account, you can write gists, and load them into the Online REPL. Try this

:load https://gist.githubusercontent.com/Ingo60/0687891988534f6875bf/raw/f44982c34bc9e0508b630208e5132ad7b91b9273/Welcome.fr

doubleSmallNumber n = if n < 100 then n else n*2

When you try this function in Frege with floating point numbers, you'll get an error message:

frege> doubleSmallNumber 76.7e-99
[ERROR: 8]: E <console>.fr:8: Int is not an instance of Real

This is because in Frege, this function will only work for integers. If you want it to be polymorphic like in Haskell, you need to add this to your script:

doubleSmallNumber :: Num a => a -> a

This is a type annotation, and it is usually written in the line before the function definition. If you don't want to do that, this is fine, as long as you restrict yourself to doubling small integers.

Note that the REPL tells you the type of a function, when you define it. When you loaded the script (without type annotations), you may have noticed this output:

frege> :load double.fr
function doubleMe :: Num a => a -> a
function doubleSmallNumber :: Int -> Int

This tells you that you can use doubleMe on any number, but doubleSmallNumber only on Int (which is the name for the primitive Java type int).


That apostrophe doesn't have any special meaning in Haskell's syntax. It's a valid character to use in a function name.

frege> conanO'Brien = "It's a-me, Conan O'Brien!" 
[ERROR: 9]: E <console>.fr:9: can't resolve `Brien`, did you mean `Bind` perhaps?

In Frege, the apostrophe is only allowed at the end of a name. (This is how it is used in Haskell code most of the time.)


An intro to lists

strings (which are lists)

Not so in Frege. We'll come back to this in a minute.


We can use the let keyword to define a name right in GHCI.

Whereas in the Frege REPL, we simply write the definition without the let, just like we would in a script.


Speaking of characters, strings are just lists of characters. "hello" is just syntactic sugar for ['h','e','l','l','o']. Because strings are lists, we can use list functions on them, which is really handy.

This is one of the more fundamental differences between Frege and Haskell. The reason is that Java has its own, quite sophisticated and versatile string type, (i.e. java.lang.String).

Despite of this, certain functions that work only on lists in Haskell do work on strings in Frege. In addition, there are functions that convert strings to lists and back, should this be needed. We'll introduce them here as the need arises.

If you are curious how the String type looks in Frege, you can use the REPL to tell you. Type :help String and browse from there. You could also directly go to the String documentation.


'A':" SMALL CAT"

frege> 'A':" SMALL CAT"
[ERROR: 11]: E <console>.fr:11: type error in  expression " SMALL CAT"
    type is   String
    used as   [Char]

From the first part of the expression 'A': the Frege type checker infers that you want to put a character in front of a list, and since lists are homogenous, it must be a character list. But on the right hand side of the :, it found a string (the type is called String), and strings are not list of characters (denoted [Char]) in Frege, as explained above.

There are two ways to fix this:

frege> "A" ++ " SMALL CAT"
A SMALL CAT

since ++ is one of the operators that work on both strings and lists. The result is a string. Alternatively, we can turn the string into a list of charcters, and then put the 'A' in front:

frege> 'A' : toList " SMALL CAT"
['A',' ','S','M','A','L','L',' ','C','A','T']

Here, the result is a list of characters.


"Steve Buscemi" !! 6

frege> "Steve Buscemi" !! 6  
[ERROR: 12]: E <console>.fr:12: type error in  expression "Steve Buscemi"
    type is   String
    used as   [t17088]

This means so much as "String is not a list at all.", which is the case in Frege. You can convert the string using toList as before. But if you're a Java programmer, you'll be glad to hear that the charAt method of java.lang.String is supported in Frege (among many other string methods). That is, you can write:

frege> "Steve Buscemi".charAt 6  
'B'

They can also contain lists that contain lists that contain lists

In the following example, remember to omit the let or else it'll be a syntax error:

frege> let b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
[ERROR: 12]: E <console>.fr:12: expected token "in", found '}'

If we think of a list as a monster, here's what's what.

Of the functions that are introduced in the rest of the section, the following do work on strings and lists alike: head, tail, take, length, null, drop.


Texas Ranges

(to be continued)

Clone this wiki locally