1
votes

I have just started learning functional programming and Haskell and am I not even close to understanding the syntax and how it works because it is very different from what I have been used to before (ruby, php, C# or node for instance, which are OOP languages - more or less).

I am am trying to put together a small program that checks for solutions for Brocard's Problem or so called Brown Numbers and I first created a draft in ruby, but I discovered that Haskell is much more appropriate for doing mathematical expressions. I've asked a question previously and I got an answer pretty quick on how to translate my ruby code to Haskell:

results :: [(Integer, Integer)] --Use instead of `Int` to fix overflow issue
results =  [(x,y) | x <- [1..1000], y <- [1..1000] , 1 + fac x == y*y]
    where fac n = product [1..n]

I changed that slightly so I can run the same operation from and up to whatever number I want, because the above will do it from 1 up to 1000 or any hardcoded number but I would like to be able to decide the interval it should go through, ergo:

pairs :: (Integer, Integer) -> [(Integer, Integer)]
pairs (lower, upper) =  [(m, n) | m <- [lower..upper], n <- [lower..upper], 1 + factorial n == m*m] where factorial n = product [1..n]

The reason I ask this questions is that I was thinking if I could run this operations in parallel (and/or on multiple threads and/or cores) and append the results to the same variable.

I have not yet understood the parallel processing in Haskell or the threading model, but in ruby a good example of how the threading model works is something like this:

def func1
  i=0
  while i<=2
    puts "func1 at: #{Time.now}"
    sleep(2)
    i=i+1
  end
end

def func2
  j=0
  while j<=2
    puts "func2 at: #{Time.now}"
    sleep(1)
    j=j+1
  end
end

puts "Started At #{Time.now}"
t1=Thread.new{func1()}
t2=Thread.new{func2()}
t1.join
t2.join
puts "End at #{Time.now}"

And in my case, the func1 and func2 would be the same function results computed on different intervals ([1..1000] -> [i..j]).

I would appreciate some help with this as I am not capable of doing it myself at this point :)

1
If you want to figure it out on your own, I'd suggest reading Parallel and Concurrent Programming in Haskell by Simon Marlow. You can buy the book or just read it online for free at that link. He has a very good coverage of the different techniques you can use in Haskell. - bheklilr

1 Answers

2
votes

Parallel and Concurrent Programming in Haskell has a lot of good information, and async is a good library for this stuff.

At the bottom level though, you'll find forkIO to start a new lightweight thread.

Of course that's concurrency, not deterministic parallelism, parallel is the library for that, and also covered in the book.

Your example translates to:

import Data.Time.Clock (getCurrentTime)

main = do
    start <- getCurrentTime
    putStr "Started At " >> print start
    _ <- forkIO func1
    _ <- forkIO func2
    end <- getCurrentTime
    putStr "End at " >> print end

func1 = helper "func1" 2

func2 = helper "func2" 1

helper name sleepTime = go 0
 where
    go 3 = return ()
    go n = do
        now <- getCurrentTime
        putStr name >> putStr " at: " >> print now
        threadDelay sleepTime
        go $ succ n

I recommend learning the parallel and/or async libraries mentioned above, though, instead of writing your own threading stuff, at least initially.

Here's a not-so-great example of running tests on 8-ish processors using parallel:

import Control.Parallel.Strategies

factorial = product . enumFromTo 1
pairs (lower, upper) =  map fst . filter snd . withStrategy sparkTest
                     $ [ ((m, n), b)
                       | m <- [lower..upper]
                       , n <- [lower..upper]
                       , let b = 1 + factorial n == m*m
                       ]
sparkTest = evalBuffer 8 $ evalTuple2 rseq rpar