-
Notifications
You must be signed in to change notification settings - Fork 145
Online REPL Walkthrough
In the online REPL, you can use the editor pane on the left hand side to insert and edit Frege code.
You can then run it by clicking the 'Evaluate' button at the bottom or by pressing 'Ctrl + Enter'.
This wiki page should provide you with some ideas that you may want to try in the Repl.
The Read-Evaluate-Print-Loop (REPL) reads one command at a time, evaluates it, prints the result, and then waits for the next input. For editing multi-line input, it is easier to use the editor in the left panel.
Special commands that modify the behavior of the REPL start with a colon :
, most notably :help
.
You can also use the Up/Down arrow keys on your keyboard to browse through the history of previously entered commands.
The online REPL is only one of many REPL applications that Frege supports. They all share the same basic behavior and the same underlying interpreter. The basic REPL is contained in the Frege distribution, which uses it with a command-line interface and a Swing-based GUI. There also is JavaFX based REPL that uses FregeFX, and the online REPL that we talk about here.
Let's go through some typical Frege features.
Start with creating a list and assign it to the reference myList
.
frege> myList = [1,2,3]
value myList :: [Int]
The REPL tells you the type of the last evaluated expression.
Read this as myList
is of type (::
) List ([ ]
) of values of type Int (Int
).
Now try to assign a new value to myList
:
frege> myList = [5,6,7]
2: E .fr:2: function binding without patterns must have only a single equation
Here E
calls out an error at line 2. You are not allowed to assign new values to
existing references. Never. (We explain the error message below.)
If you changed your mind, what myList
should be, you either have to give it a new name
or reset the evaluation context:
frege> :reset
frege> myList = [5,6,7]
value myList :: [Int]
After reset, your command history is still intact but all previously known references have gone.
You can use :browse
to see the currently known references:
frege> :browse
value myList :: [Int]
frege> :reset
frege> :browse
When we tried to assign a new value to 'myList' we got the error message function binding without patterns must have only a single equation
. This needs a bit of explanation.
Since all references in Frege are immutable, we do not even get an error message like you cannot change an immutable value
. That's supposed to be obvious.
But there are cases when it looks like as if you would have multiple assignments. They occur for example when defining a function like multiply m n = m * n
and you would like to care for the special value 0
, where we know the answer without calculating.
multiply m 0 = 0
multiply 0 n = 0
multiply m n = m * n
Here, we use so-called patterns in the parameter list of the multiply function to short-circuit the special cases.
If you view myList
not as a constant but as a function with no parameters, then it can only be defined with one expression since no patterns can possibly apply. Hence the error message. In most cases where this error message occurs, it is actually more
likely that you forgot to add a parameter.
BTW: in a pure language like Frege where functions have no visible side effects and return the same value if given the same arguments, any function without arguments must return a constant value.
Frege is lazy - or to put it in formal terms - Frege evaluates in a non-strict approach. That means that arguments to a function call are not evaluated before the function call (strictly) but passed as expressions that can be evaluated later (and in many cases might never be evaluated).
This allows for some nice effects in the REPL. For example [1..]
generates an infinite list that would normally take infinite time to print but here is what happens in the REPL:
frege> [1..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,3
Since we only need the first 30 numbers to print a result of 80 characters, the remaining numbers are never calculated.
This leads to some interesting consequences.
The classical example is the endless list of Fibonacci numbers as
fib = 1n : 1n : zipWith (+) fib (tail fib)
frege> fib
[1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,
Some notes:
-
:
cons operator, prepend list with value -
1n
is the value 1 but asInteger
(as opposed toInt
), which is mapped to Java's BigInteger -
zipWith
see API doc -
(+)
sectioning, same as(\x y -> x + y)
-
tail
see API doc
The mind-expanding effect here is how the laziness of evaluation allows for referring to "values" that will only become available later as part of the calculation.
We can combine two strings to form another string.
We can combine two lists to form another list.
We can combine two numbers to form another number.
We can combine two functions to form another function.
And so on...
You see the pattern. We have some a
and another a
, combine them and get another a
.
It is as easy and natural as Lego bricks and a construction that we just love in programming.
In Frege, our combination operator is <>
(which is short for the function mappend
) and it lives
in the module Data.Monoid
:
frege> import Data.Monoid
2: H .fr:2: unused import of Data.Monoid as Monoid
frege> "a" <> "b"
ab
frege> [1,2] <> [3,4]
[1,2,3,4]
frege> (+1) <> (+2) <> (/2)
Double->Double
Your are missing the numbers? We will come to this in a second.
Now, can we exploit this pattern to write a useful function that works for all such examples? Yes, we can.
Let's use the fold
function to merge all entries in a list to produce a final result.
We start with summing up (+)
, starting at 0
, a list of numbers.
frege> fold (+) 0 [1,2,3,4]
10
We can extend this to applying our combination operator to our examples above:
frege> fold (<>) "" ["a","b"]
ab
frege> fold (<>) [] [ [1,2], [3,4] ]
[1,2,3,4]
frege> fold (<>) id [ (+1), (+2), (/2) ]
Double->Double
Nice, this now works for an arbitrary number of things to combine.
But can we remove the duplication? All examples share the same operator, but the start value is
different. Luckily, all such types (Monoids) also have a neutral element that we can start with.
It is called mempty
.
frege> merge = fold (<>) mempty
function merge :: Monoid a => [a] -> a
frege> merge ["a","b"]
ab
frege> merge [ [1,2], [3,4] ]
[1,2,3,4]
The rather surprising fact is that in the definition of merge
we don't even know what type of
list we will get: a list of strings, of lists, of functions? We only know it will be a list of some
monoidal type.
In other languages you might think of Monoid as an interface, used as a generic type parameter like in
List<T extends Monoid>
but those are intrusively attached to your class at definition time. Frege is more flexible.
Why haven't we used any plain numbers, yet? That's because they are monoidal in more than one way and we have to tell, which version we want:
- numbers form a Monoid with
+
and0
- numbers form a Monoid with
*
and1
In order to distinguish between the two, we can wrap numbers in a newtype (a construct that
has no runtime overhead but allows distinction at compiletime). They are readily available in
module Data.wrapper.Num
from which we use the monoidal Sum
wrapper:
frege> import Data.wrapper.Num
4: H .fr:4: unused import of Data.wrapper.Num as Num
frege> nums = map Sum [1,2,3,4]
value nums :: [Sum Int]
frege> merge nums
Sum 10
Please note:
- we can easily extract repeating patterns
- the function dispatch is safe and predictable (no runtime trickery, reflection/dynamic invocation, or implicits)
- we can make an existing type monoidal without touching its definition!
- There is more to know about Monoids. They must obey laws.
Frege comes with an integrated testing facility called QuickCheck
, which is modeled after
the Haskell project of the same name. There is a Paper by John Hughes that nicely explains the concept.
There is also Frege-specific information about QuickCheck in the free FregeGoodness ebook.
Here is a short introduction on how to use QuickCheck in the online REPL.
First, import the module:
frege> import Test.QuickCheck
3: H .fr:3: unused import of Test.QuickCheck as QuickCheck
Now we can test so-called properties. Properties are expressions that we always expect to be true.
We can check those with the quickCheck
function:
frege> quickCheck true
()
+++ OK, passed 1 tests.
(0 tests)
frege> quickCheck $ 45 / 9 == 5
()
+++ OK, passed 1 tests.
(0 tests)
This is nice for fixed values but QuickCheck can do much more! Our property can be based on arbitrary values, in which case we use a function expression.
Let's check our assumption that every integral square number n
can be derived from n-1
:
frege> quickCheck $ \n -> n * n == (n-1)*(n-1) + 2*(n-1) + 1
()
+++ OK, passed 100 tests.
So far, so good. But what happens when we do it wrong?
frege> quickCheck $ \n -> n * n == n * n + 2 * n
()
*** Failed! (after 2 tests and 1 shrink): Falsifiable 1
(0 tests) (1 test) Falsifiable (after 2 tests)...
QuickCheck tells us that it found a counter example 1
that falsifies our property.
But 1
was not the first example that failed. Only after shrinking (finding the "smallest" value
that makes the property false) do we land at 1
.
Corollary: We did not shrink down to
0
from which we can conclude that the property holds for0
but not for1
. That is often a good indicator to find the error.
Your might wonder how QuickCheck even knew that it had to produce numbers to feed into our expression. Well, this is again a cool capability of type inference - in our case based on the fact that we use numerical operators.
But QuickCheck can deal with much more complex types, for example list of characters.
We should be able to pack
them into Strings and have them unpacked
to list of characters:
frege> quickCheck $ \cs -> cs == unpacked(packed cs)
()
+++ OK, passed 100 tests.
But if we add a reverse we should see it fail, and it does:
frege> quickCheck $ \cs -> cs == reverse(unpacked(packed cs))
()
*** Failed! (after 3 tests and 2 shrinks): Falsifiable "ab"
(0 tests) (1 test) (2 tests) Falsifiable (after 3 tests)...
Note how it fails with "ab", meaning it can not shrink down to "aa", "a", or "" since those pass.
There is much more to say about QuickCheck but we save this for later time.
Here comes an arguably more real-life example from an every-day programmer's task list: create a JSON representation of a given data structure.
Copy the following code in the editor and evaluate it.
This will show the :load editor
command in the repl and some evaluation result.
You can now run the run function by typing run
in the repl.
module examples.JSONExample where
import Data.JSON (toJSON)
--- Simple data structure to capture a coordinate as latitude and longitude.
data Coordinate = Coordinate
{ lon :: Float
, lat :: Float
}
--- Composed data structure for a weather observation at a location
data Weather = Weather
{ loc :: Coordinate
, temp :: Int
, desc :: String
, alert :: Maybe String
}
--- How coordinates marshal to JSON
instance ToJSON Coordinate where
toJSON coord = Struct
[ ("lon", toJSON coord.lon)
, ("lat", toJSON coord.lat)
]
--- How weather observations marshal to JSON, including nested location coordinates
instance ToJSON Weather where
toJSON Weather{loc, temp, desc, alert} = Struct
[ ("loc", toJSON loc)
, ("temp", toJSON temp)
, ("desc", toJSON desc)
, ("alert", toJSON $ fromMaybe "null" alert)
]
-- example use
run = toJSON $ Weather location 98 "overcast clouds" (Just "Excessive heat")
where
location = Coordinate { lon = -122.09, lat = 37.39 }
You will find many other Frege sources that you can try in the online REPL in various places:
- the links section on the home page
- the frege core examples
- github written in Frege
Home
News
Community
- Online Communities
- Frege Ecosystem
- Frege Day 2015
- Protocol
- Simon Peyton-Jones Transcript
- Talks
- Articles
- Books
- Courses
Documentation
- Getting Started
- Online REPL
- FAQ
- Language and API Reference
- Libraries
- Language Interoperability
- Calling Frege From Java (old)
- Calling Java From Frege
- Calling Frege From Java (new)
- Compiler Manpage
- Source Code Doc
- Contributing
- System Properties
- License
- IDE Support
- Eclipse
- Intellij
- VS Code and Language Server
- Haskell
- Differences
- GHC Options vs Frege
- Learn You A Haskell Adaptations
- Official Doc
Downloads