Last active
June 13, 2019 03:59
-
-
Save renatoathaydes/5078535 to your computer and use it in GitHub Desktop.
Haskell VS Groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 ) | |
} | |
} | |
} | |
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. :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 }