I am implementing some algorithm on haskell. This algorithm requires generating some data.
I have a function of an algorithm which takes generation function as a parameter. For example, algorithm is just multiplying input data by n:
algo :: a -> ??? -> [a]
algo n dgf = map (\x -> x * n) $ dgf
dgf
is used to generate data. How to write function header correctly, as dgf
can be any function with any number of parameters?
Another variant is accepting not the generation function but already generated data.
algo :: a -> [b] -> [a]
algo n d = (\x -> n*x) d
So, now let's imagine I'm generation data with stdGen
, which uses IO. How can I make function more generic, so that it could accept both IO instance and plain values like just [1,2,3]
. This also relates to variant with function, as it can also produce IO.
All in all, which solution is better - having a generation function or a pre-generated data?
Thanks in advance.
For contrast, I'm going to take the opposite approach as dfeuer's answer.
Just use lists.
Consider your first example:
You ask "How to write function header correctly, as dgf can be any function with any number of parameters?"
Well, one way is to use uncurrying.
Normally, Haskell functions are curried. If we have a function like
And we want a function that adds two to its input we can just use
add 2
.Because
add
is not actually a function that takes two arguments, it's a function of one argument that returns a function of one argument.We could have added parentheses to the argument of add above to make this more clear:
In Haskell, all functions are functions of one argument.
However, we can also go the other way -
uncurry
a function that returns a function to get a function that takes a pair:This can also be useful, say if we want to find the sum of each pair in a list:
In general, we can uncurry any function of type
a0-> a1 -> ... -> aN -> b
into a function(a0, a1, ..., aN) -> b
, though there might not be a cute library function to do it for us.With that in mind, we could implement
algo
by passing it an uncurried function and a tuple of values:And then use anonymous functions to uncurry our argument functions:
Now we could do it this way, but we don't need to. As implemented above,
algo
is only usingf
andt
once. So why not pass it the list directly?It calculates the same results:
Furthermore, since haskell is non-strict, the argument to
algo'
isn't evaluated until it's actually used, so we don't have to worry about "wasting" time computing arguments that won't actually be used:algo''
doesn't use the list passed to it, so it's never forced, so whatever computation is used to calculate it never runs:Now to the second part of your question - what if your data is being generated within some monad?
Rather than convince
algo
to operate on monadic values, you could take the stream based approach as dfeuer explains. Or you could just use a list.Just because you're in a monad, doesn't mean that your values suddenly become strict.
For example, want a infinite list of random numbers? No problem.
Now I can just pass those to some algorithm:
For a small program which is just reading input from a file or two, there's no problem with just using
readFile
and lazy I/O to get a list to operate on.For example