-
-
Save renatoathaydes/5078535 to your computer and use it in GitHub Desktop.
// This is a comparison between Haskell and Groovy based on some simple problems found in the following | |
// Haskell Introduction: | |
// http://learnyouahaskell.com/starting-out#ready-set-go | |
// Ex 1. If we have two lists, [2,5,10] and [8,10,11] and we want to get the products of all the possible | |
// combinations between numbers in those lists, here's what we'd do. | |
/* HASKELL */ | |
[ x*y | x <- [2,5,10], y <- [8,10,11]] | |
/* Groovy */ | |
{ [2,5,10].collect { x -> [8,10,11].collect { y -> x * y } }.flatten() } | |
// Ex 2. What if we wanted all possible products that are more than 50? | |
/* HASKELL */ | |
[ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50] | |
/* Groovy */ | |
{ [2,5,10].collect { x -> [8,10,11].collect { y -> x * y } }.flatten().findAll{ it > 50 } } | |
// Ex 3. How about a list comprehension that combines a list of adjectives and a list of nouns … for epic hilarity. | |
/* HASKELL */ | |
[adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns] | |
/* Groovy */ | |
{ nouns, adjectives -> nouns.collect { noun -> adjectives.collect { adjective -> "$noun $adjective" } }.flatten() } | |
// Ex 4. Let's write our own version of length! We'll call it length'. | |
/* HASKELL */ | |
length' xs = sum [1 | _ <- xs] | |
/* Groovy */ | |
length = { xs -> xs.inject { acc, _ -> acc += 1 } } | |
// Ex 5. Here's a function that takes a string and removes everything except uppercase letters from it. | |
/* HASKELL */ | |
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] | |
/* Groovy */ | |
removeNonUppercase = { st -> st.inject { acc, c -> acc + ( ('A'..'Z').contains( c ) ? c : '' ) } } | |
// Ex 6. A list contains several lists of numbers. Let's remove all odd numbers without flattening the list. | |
/* HASKELL */ | |
[ [ x | x <- xs, even x ] | xs <- xxs] | |
/* Groovy */ | |
{ xxs -> xxs.inject([]) { res, xs -> res << xs.findAll { x -> x % 2 == 0 } } } | |
// Ex 7. ... let's try generating all triangles with sides equal to or smaller than 10: | |
/* HASKELL */ | |
let triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ] | |
/* Groovy */ | |
def triangles = { def res = [] | |
(1..10).each{ a -> (1..10).each{ b -> (1..10).each{ c -> res << [a,b,c] } } } | |
res | |
} | |
// Ex 8. ... we'll add a condition that they all have to be right triangles. | |
// We'll also modify this function by taking into consideration that side b isn't larger | |
// than the hypothenuse and that side a isn't larger than side b. | |
/* HASKELL */ | |
let rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2] | |
/* Groovy */ | |
def rightTriangles = { def res = [] | |
(1..10).each{ c -> (1..c).each{ b -> (1..b).each{ a -> if ( a**2 + b**2 == c**2 ) res << [a,b,c] } } } | |
res | |
} | |
//############### Doing some I/O ###############// | |
// Ex 9. Read file and print its contents | |
/* HASKELL */ | |
import System.IO | |
main = do | |
contents <- readFile "girlfriend.txt" | |
putStr contents | |
/* Groovy */ | |
new File("girlfriend.txt").eachLine{ println it } | |
// Ex 10. Convert a file's text to ALL CAPS text and save it in another file | |
/* HASKELL */ | |
import System.IO | |
import Data.Char | |
main = do | |
contents <- readFile "girlfriend.txt" | |
writeFile "girlfriendcaps.txt" (map toUpper contents) | |
/* Groovy */ | |
def input = new File("girlfriend.txt") | |
new File("girlfriendcaps.txt").withWriter { w -> input.eachLine{ w.writeLine it.toUpperCase() } } | |
// Ex 11. Given a text file containing TO-DO tasks, remove a line chosen by the user from the file | |
/* HASKELL */ | |
import System.IO | |
import System.Directory | |
import Data.List | |
main = do | |
handle <- openFile "todo.txt" ReadMode | |
(tempName, tempHandle) <- openTempFile "." "temp" | |
contents <- hGetContents handle | |
let todoTasks = lines contents | |
numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks | |
putStrLn "These are your TO-DO items:" | |
putStr $ unlines numberedTasks | |
putStrLn "Which one do you want to delete?" | |
numberString <- getLine | |
let number = read numberString | |
newTodoItems = delete (todoTasks !! number) todoTasks | |
hPutStr tempHandle $ unlines newTodoItems | |
hClose handle | |
hClose tempHandle | |
removeFile "todo.txt" | |
renameFile tempName "todo.txt" | |
/* Groovy */ | |
def todoTasks = [] | |
def todoFile = new File('/tmp/todo.txt') | |
todoFile.eachLine { todoTasks << it } | |
println 'These are your TO-DO items:' | |
todoTasks.eachWithIndex { todo, index -> | |
println "${index + 1} - $todo" | |
} | |
def numberString = System.console().readLine 'Which one do you want to delete?' | |
todoTasks.remove( numberString.toInteger() - 1 ) | |
todoFile.withWriter { out -> | |
todoTasks.each { out.writeLine it } | |
} | |
// Ex 12. Create a SQL database with a single table, add data to it, then print the contents of the table | |
// Based on http://book.realworldhaskell.org/read/using-databases.html | |
/* HASKELL */ | |
// Using Sqlite database for convenience: http://www.sqlite.org/ | |
:module Database.HDBC Database.HDBC.Sqlite3 | |
conn <- connectSqlite3 "test1.db" | |
run conn "CREATE TABLE test (id INTEGER NOT NULL, desc VARCHAR(80))" [] | |
run conn "INSERT INTO test (id, desc) VALUES (?, ?)" [toSql 123, toSql "DB stuff"] | |
commit conn | |
res <- quickQuery conn "SELECT * FROM test" | |
let stringRows = map convRow res | |
mapM_ putStrLn stringRows | |
disconnect conn | |
where convRow :: [SqlValue] -> String | |
convRow [sqlId, sqlDesc] = | |
show intid ++ ": " ++ desc | |
where intid = (fromSql sqlId)::Integer | |
desc = case fromSql sqlDesc of | |
Just x -> x | |
Nothing -> "NULL" | |
convRow x = fail $ "Unexpected result: " ++ show x | |
/* Groovy */ | |
// Much more convenient to use H2 in Java/Groovy: http://www.h2database.com | |
import static groovy.sql.Sql.newInstance | |
def sql = newInstance 'jdbc:h2:~/testdb', 'username', 'password', 'org.h2.Driver' | |
sql.execute 'CREATE TABLE test (id INTEGER NOT NULL, desc VARCHAR(80))' | |
sql.execute 'INSERT INTO test (id, desc) VALUES (?, ?)', [123, 'DB stuff'] | |
sql.eachRow( 'SELECT * FROM test' ) { println "${it.id}: ${it.desc}" } | |
//############### GUI Stuff ###############// | |
// Ex 13. Create a window with a menu-bar containing a File menu with 2 items - About.. and Quit | |
// At the bottom of the window, there should be a status bar displaying some help for the currently | |
// selected menu item. | |
/* HASKELL */ | |
{- demonstrates the use of a simple menu, statusbar, and dialog -} | |
module Main where | |
import Graphics.UI.WX | |
main :: IO () | |
main | |
= start hello | |
hello :: IO () | |
hello | |
= do -- the application frame | |
f <- frame [text := "Hello world!", clientSize := sz 300 200] | |
-- create file menu | |
file <- menuPane [text := "&File"] | |
quit <- menuQuit file [help := "Quit the demo", on command := close f] | |
-- create Help menu | |
hlp <- menuHelp [] | |
about <- menuAbout hlp [help := "About wxHaskell"] | |
-- create statusbar field | |
status <- statusField [text := "Welcome to wxHaskell"] | |
-- set the statusbar and menubar | |
set f [ statusBar := [status] | |
, menuBar := [file,hlp] | |
-- as an example, put the menu event handler for an about box on the frame. | |
,on (menu about) := infoDialog f "About wxHaskell" "This is a wxHaskell demo" | |
] | |
/* Groovy */ | |
import groovy.swing.SwingBuilder | |
import java.awt.BorderLayout as BL | |
import static javax.swing.JOptionPane.showMessageDialog as showDialog | |
import static javax.swing.JOptionPane.INFORMATION_MESSAGE as INFO | |
def about = { showDialog null, "This is a Groovy demo", "About Groovy", INFO } | |
new SwingBuilder().edt { | |
def status | |
def defaultStatus = "Welcome to Groovy SwingBuilder" | |
frame( title:'Hello world!', size: [ 300, 200 ], show: true) { | |
borderLayout() | |
menuBar() { | |
menu( text: "File" ) { | |
menuItem( text: "About..", | |
actionPerformed: about, | |
mouseEntered: { status.text = "About Groovy" }, | |
mouseExited: { status.text = defaultStatus } ) | |
menuItem( text: "Quit", | |
actionPerformed: { dispose() }, | |
mouseEntered: { status.text = "Quit the demo" }, | |
mouseExited: { status.text = defaultStatus } ) | |
} | |
} | |
panel( constraints: BL.SOUTH ) { | |
loweredBevelBorder( parent: true ) | |
status = label( text: defaultStatus ) | |
} | |
} | |
} | |
Added I/O examples
Added database access code
The Groovy database Example (Ex 12) can actually be run without downloading anything, not even the db, by adding this to the script:
@GrabConfig(systemClassLoader=true, initContextClassLoader=true)
@grab(group="com.h2database", module="h2", version="1.3.170")
Added GUI example (Ex 13). Based on the Hello World example from WxHaskell (see '4. Documentation -> Screenshots -> Samples' section):
http://www.haskell.org/haskellwiki/WxHaskell
Example 1 in Groovy can be written like this:
[[2,5,10],[8,10,11]].combinations().collect { it[0]*it[1] }
or
[[2,5,10],[8,10,11]].combinations().collect { x,y->x*y }
Same for noun/adjective:
{ adjectives, nouns -> [adjectives, nouns].combinations().collect { it.join ' ' } }
Example 10 in Haskell can be written like this:
import System.IO
import Data.Char
import Control.Applicative
main = map toUpper <$> readFile "out.txt" >>= writeFile "outcaps.txt"
I wrote Scala VS Ceylon using the same examples: https://gist.github.com/renatoathaydes/7997321
I wrote groovy-stream to give easier access to comprehension like lazy sequences. Here's Ex 1-8 using it (skipped 4 as stream wouldn't help)
// Groovy-stream versions of https://gist.github.com/renatoathaydes/5078535
@Grab( 'com.bloidonia:groovy-stream:0.7.4' )
import groovy.stream.*
// Ex 1. If we have two lists, [2,5,10] and [8,10,11] and we want to get the products of all the possible
// combinations between numbers in those lists, here's what we'd do.
Stream.from( x:[2,5,10], y:[8,9,10] ).map { x * y }.collect()
// Ex 2. What if we wanted all possible products that are more than 50?
Stream.from( x:[2,5,10], y:[8,9,10] ).map { x * y }.filter { it > 50 }.collect()
// Ex 3. How about a list comprehension that combines a list of adjectives and a list of nouns … for epic hilarity.
def adjectives = [ 'perfumed', 'dry' ]
def nouns = [ 'cat', 'book', 'horse' ]
Stream.from( a:adjectives, n:nouns ).map { "$a $n" }.collect()
// Ex 5. Here's a function that takes a string and removes everything except uppercase letters from it.
def st = 'Hello World'
Stream.from( st.toList() ).filter { ('A'..'Z').contains( it ) }.join()
// Ex 6. A list contains several lists of numbers. Let's remove all odd numbers without flattening the list.
Stream.from( [ 1..3, 4..6, 7..9 ] ).map { it.findAll { !(it % 2) } }.collect()
// Ex 7. ... let's try generating all triangles with sides equal to or smaller than 10:
Stream.from( a:1..10, b:1..10, c:1..10 ).map { [ a, b, c ] }.collect()
// Ex 8. ... we'll add a condition that they all have to be right triangles.
// We'll also modify this function by taking into consideration that side b isn't larger
// than the hypothenuse and that side a isn't larger than side b.
Stream.from( c:1..10, b:1..10, a:1..10 ).filter { b <= c && a <= b }.filter { a**2 + b**2 == c**2 }.map { [ a, b, c ] }.collect()
A shorter solution for Ex. 11 in Haskell
import System.IO
import Data.List
main = do
let file = "todo.txt"
contents <- readFile file
let todoTasks = lines contents
numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks
mapM putStrLn $ "These are your TO-DO items:" : numberedTasks ++ ["Which one do you want to delete?"]
numberString <- getLine
let number = read numberString
newTodoItems = delete (todoTasks !! number) todoTasks
writeFile file $ unlines newTodoItems
Additional Ex1. Groovy alternatives:
[[2,5,10],[8,10,11]].combinations { it[0]*it[1] }
or:
[[2,5,10],[8,10,11]].combinations { x, y -> x * y }
Haha, was curious about the differences between Groovy vs Haskell so i googled it.
Was not particularly surprised when i realized i ended up at one of your gists @renatoathaydes. :)
Made Groovy Ex 6. solution more functional