crazy4groovy
3/15/2013 - 12:59 AM

HaskellVSGroovy.groovy

// 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 )
    }
  }
}