I'd also say that reading command-line flags inside a simple function like amount is a pretty large code smell. The only case in which it isn't would be when the codebase is so small that redesigning the Haskell to be in IO (or switch between amountPlus and amountTimes) is negligible anyway. On Jun 26, 2012, at 18:59, Ozgun Ataman <[hidden email]> wrote:
_______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Tillmann Rendel-5
On Tue, Jun 26, 2012 at 3:19 PM, Tillmann Rendel
<[hidden email]> wrote: > A function to add up all integers in a tree: > > amount:: Tree -> Integer > amount (Leaf x) = x > amount (Branch t1 t2) = amountt1 + amountt2 > > All fine so far. Now, consider the following additional requirement: "If the > command-line flag --multiply is set, the function amount computes the > product instead of the sum." > > How would you implement this requirement in Haskell without changing the > line "amount (Leaf x) = x"? One option is to encode the desired behavior at the type level. By extended the data type slightly and adding a Functor instance, selecting between a product and a sum can be done using their Monoid newtypes: import Data.Monoid import System.Environment data Tree a = Leaf a | Branch (Tree a) (Tree a) instance Functor Tree where f `fmap` Leaf x = Leaf (f x) f `fmap` Branch x y = Branch (fmap f x) (fmap f y) amount :: Monoid a => Tree a -> a amount (Leaf x) = x amount (Branch t1 t2) = amount t1 <> amount t2 main :: IO () main = do args <- getArgs let val :: Tree Int val = Branch (Leaf 8) (Leaf 18) let getResult :: Tree Int -> Int getResult = case args of ["--multiply"] -> getProduct . amount . fmap Product _ -> getSum . amount . fmap Sum print . getResult $ val _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Tillmann Rendel-5
Hi Rico,
Rico Moorman wrote: >> data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer) >> >> amount:: Tree -> Integer >> amount (Leaf x) = x >> amount (Branch t1 t2) = amountt1 + amountt2 >> >> [...] additional requirement: "If the command-line flag --multiply is set, >> the function amount computes the product instead of the sum." >> >> How would you implement this requirement in Haskell without changing the >> line "amount (Leaf x) = x"? > The (for me at least) most obvious way to do this would be, to make the > operation to be applied to determine the amount (+ or *) an explicit > parameter in the function's definition. > > data Tree a = Leaf a > | Branch (Tree a) (Tree a) > amount :: (a -> a -> a) -> Tree a -> a > amount fun (Leaf x) = x > amount fun (Branch t1 t2) = amount fun t1 `fun` amount fun t2 I agree: This is the most obvious way, and also a very good way. I would probably do it like this. > Which drawbacks do you see besides increased verbosity? Well, you did change the equation "amount (Leaf x) = x" to "amount fun (Leaf x) = x". In a larger example, this means that you need to change many lines of many functions, just to get the the value of fun from the point where it is known to the point where you need it. > [...] I am wondering which ways of doing this in Haskell you mean. I thought of the following three options, but see also Nathan Howells email for another alternative (that is related to my option (1) below): (1) Implicit parameters: {-# LANGUAGE ImplicitParams #-} data Tree = Leaf Integer | Branch Tree Tree amount :: (?fun :: Integer -> Integer -> Integer) => Tree -> Integer amount (Leaf x) = x amount (Branch t1 t2) = ?fun (amount t1) (amount t2) (2) Lexical Scoping: data Tree = Leaf Integer | Branch Tree Tree amount :: (Integer -> Integer -> Integer) -> Tree -> Integer amount fun = amount where { amount (Leaf x) = x ; amount (Branch t1 t2) = fun (amount t1) (amount t2) } (3) UnsafePerformIO: import System.IO.Unsafe (unsafePerformIO) data Tree = Leaf Integer | Branch Tree Tree amount :: Tree -> Integer amount (Leaf x) = x amount (Branch t1 t2) = fun (amount t1) (amount t2) where fun = unsafePerformIO ... I'm not happy with any of these options. Personally, I would probably go ahead and transform the whole program just to get the value of fun to where it is needed. Nevertheless, having actually done this before, I understand why Martin Odersky doesn't like doing it :) Tillmann _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
> I'm not happy with any of these options.
Why are you unhappy with the ImplicitParams option? It's pretty much like resorting to a newtype, as it's been suggested before.
2012/6/27 Tillmann Rendel <[hidden email]> Hi Rico, _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
The "problem" of monads is that it defines different execution models, besides the funcional,/lazy/declarative mode. There is no such problem in imperative languages, which work ever in an hardwired IO monad. But this means that the programmer has to code the extra behaviour needed in each application to do the same.
I summarized this here:
http://haskell-web.blogspot.com.es/2012/06/intuitive-explanation-of-algorithms-in.html It pretend to be intuitive, not accurate. (See disclaimer ;) . Comments welcome
2012/6/27 Yves Parès <[hidden email]>
_______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Tillmann Rendel-5
Tillmann Rendel <rendel <at> informatik.uni-marburg.de> writes:
> > > Which drawbacks do you see besides increased verbosity? > > Well, you did change the equation "amount (Leaf x) = x" to "amount fun > (Leaf x) = x". In a larger example, this means that you need to change > many lines of many functions, just to get the the value of fun from the > point where it is known to the point where you need it. I would argue that no matter how good one's language is, there will always exist realistic refactorings that require you to make sweeping changes to a large portion of your code base. > > [...] I am wondering which ways of doing this in Haskell you mean. > > I thought of the following three options, but see also Nathan Howells > email for another alternative (that is related to my option (1) below): > > (1) Implicit parameters: <snip> > (2) Lexical Scoping: <snip> > (3) UnsafePerformIO: <snip> > I'm not happy with any of these options. Personally, I would probably go > ahead and transform the whole program just to get the value of fun to > where it is needed. Nevertheless, having actually done this before, I > understand why Martin Odersky doesn't like doing it :) I think that Martin's statement is an unavoidable fact of life that follows directly from the definition of purity and the mechanics of data dependencies. Of course nobody likes making sweeping changes to their app because they didn't anticipate the way the future would evolve. But lets not blame monads for what is really a much more fundamental phenomenon. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Nathan Howell-2
On 26/06/2012, Nathan Howell <[hidden email]> wrote:
> On Tue, Jun 26, 2012 at 3:19 PM, Tillmann Rendel > <[hidden email]> wrote: >> All fine so far. Now, consider the following additional requirement: "If >> the >> command-line flag --multiply is set, the function amount computes the >> product instead of the sum." >> >> How would you implement this requirement in Haskell without changing the >> line "amount (Leaf x) = x"? > > One option is to encode the desired behavior at the type level. By > extended the data type slightly and adding a Functor instance, > selecting between a product and a sum can be done using their Monoid > newtypes: ... Better yet, use foldMap: > import Data.Monoid > import Data.Foldable > import System.Environment > data Tree a = Leaf a | Branch (Tree a) (Tree a) > instance Functor Tree where > f `fmap` Leaf x = Leaf (f x) > f `fmap` Branch x y = Branch (fmap f x) (fmap f y) > instance Foldable Tree where > foldMap f (Leaf x) = f x > foldMap f (Branch s t) = foldMap f s <> foldMap f t > main :: IO () > main = do > args <- getArgs > let val :: Tree Int > val = Branch (Leaf 8) (Leaf 18) > let getResult :: Tree Int -> Int > getResult = case args of > ["--multiply"] -> getProduct . foldMap Product > _ -> getSum . foldMap Sum > print . getResult $ val Yet better yet: > {-# LANGUAGE DeriveFunctor, DeriveFoldable #-} > import Data.Monoid; > import Data.Foldable; > import System.Environment > data Tree a = Leaf a | Branch (Tree a) (Tree a) > deriving (Functor, Foldable); > ... (^_^) -- Strake _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Jonathan Geddes
To be fair, while it's great that Functor et al. are available to
help solve problems like that with a Tree data structure, in the present context it mostly shows that a trivial example was used to illustrate the problem. Unless I'm missing how this generic fold functionality solves the more general problem. Quoth Alberto G. Corona, > ... There is no such problem in imperative languages, which work > ever in an hardwired IO monad. Or to look at it from the other direction, the problem with Haskell is that it allows the programmer to write non-IO functions. To the extent that's a real problem, the solution would obviously be to exercise some restraint with the pure code and write more in the IO monad. If you will humor me, we understand that this code is equally satisfactory from a mathematical perspective and there's no crime in it. But does it help with the `flags problem?' Suppose that functions f and g both have IO types; f calls g, and g has optional behavior depending on some IO value computed previously, the flag. The way I understand it, I can accomplish this without passing the flag around: with an IORef. What's less clear to me is whether Haskell really supports the module level accessible IORef value I need. Also known as "top level mutable state." Is the NOINLINE pragma I've been using with GHC, really a sound and reliable practice that we're reasonably confident will be supported by other compilers? It feels to me like I'm pulling a weird trick, to do something that's trivial in other programming languages. Maybe that's part of the problem, that imperative programming receives something a little short of full support in Haskell, inasmuch as basic features like top level mutable state require relatively arcane incantations. Donn _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Tillmann Rendel-5
2012/6/27 Tillmann Rendel <[hidden email]>:
> MightyByte wrote: >> >> Of course every line of your program that uses a Foo will change if you >> switch >> to IO Foo instead. > > > But we often have to also change lines that don't use Foo at all. For > example, here is the type of binary trees of integers: > > data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer) > > A function to add up all integers in a tree: > > amount:: Tree -> Integer > amount (Leaf x) = x > amount (Branch t1 t2) = amountt1 + amountt2 > > All fine so far. Now, consider the following additional requirement: "If the > command-line flag --multiply is set, the function amount computes the > product instead of the sum." > > In a language with implicit side effects, it is easy to implement this. We > just change the third line of the amount function to check whether to call > (+) or (*). In particular, we would not touch the other two lines. > > How would you implement this requirement in Haskell without changing the > line "amount (Leaf x) = x"? I may be missing the point here, but having worked on large code bases with a wide variety contributors before, I find it very advantageous that programmers are prevented from writing an amount function whose behaviour depends on command line arguments without at least an indication in the type. The fact that the function can not perform stuff like that is precisely the guarantee that the Haskell type gives me... Dominique _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Any idea whether Martin Odersky has read this discussion?
Thanks and regards, -Damodar Kulkarni On Fri, Jun 29, 2012 at 12:23 AM, Dominique Devriese <[hidden email]> wrote: 2012/6/27 Tillmann Rendel <[hidden email]>: _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Tillmann Rendel-5
On Tue, Jun 26, 2012 at 6:19 PM, Tillmann Rendel
<[hidden email]> wrote: > How would you implement this requirement in Haskell without changing the > line "amount (Leaf x) = x"? The hflags library [http://hackage.haskell.org/package/hflags] seems to do that, however... > (I actually see three ways of doing this in Haskell, but all have serious > drawbacks and do not fully solve the problem). ...it uses the unsafe IO trick [http://www.haskell.org/haskellwiki/Top_level_mutable_state], which may be one of those three ways you aren't fond of. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Dominique Devriese-2
On Thu, Jun 28, 2012 at 2:53 PM, Dominique Devriese
<[hidden email]> wrote: > 2012/6/27 Tillmann Rendel <[hidden email]>: >> How would you implement this requirement in Haskell without changing the >> line "amount (Leaf x) = x"? > > I may be missing the point here, but having worked on large code bases > with a wide variety contributors before, I find it very advantageous > that programmers are prevented from writing an amount function whose > behaviour depends on command line arguments without at least an > indication in the type. The fact that the function can not perform > stuff like that is precisely the guarantee that the Haskell type gives > me... I don't think there's an answer that's uniformly right; it depends on whether you think of the input to the program, e.g. the environment, command-line arguments, etc. as 'constant' and in some sense, pure. The latter are constant in the sense that they never change, but they are not fixed at compile-time. Other languages effectively treat them as pure (by passing them directly to main), whereas Haskell chooses not to, which is probably the reason why getArgs has IO in its type (something that seems unintuitive at first.) That precedent supports the view that e.g. a command-line flag shouldn't affect behavior without the type reflecting it, e.g. by doing IO, but the de facto use of the unsafe IO trick means not everyone agrees. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Jonathan Geddes
> That precedent supports the view that e.g. a command-line flag
> shouldn't affect behavior without the type reflecting it, e.g. by > doing IO, but the de facto use of the unsafe IO trick means not > everyone agrees. For those interested, here's [1] a case where treating command line arguments as top level constants went wrong. Look specifically at the section named "Crime Doesn't Pay". Best, Facundo [1] http://www.aosabook.org/en/ghc.html > Date: Tue, 3 Jul 2012 17:49:48 -0400 > From: Alvaro Gutierrez <[hidden email]> > Subject: Re: [Haskell-cafe] Martin Odersky on "What's wrong with > Monads" > To: Dominique Devriese <[hidden email]> > Cc: [hidden email] > Message-ID: > <[hidden email]> > Content-Type: text/plain; charset=ISO-8859-1 > > On Thu, Jun 28, 2012 at 2:53 PM, Dominique Devriese > <[hidden email]> wrote: >> 2012/6/27 Tillmann Rendel <[hidden email]>: >>> How would you implement this requirement in Haskell without changing the >>> line "amount (Leaf x) = x"? >> >> I may be missing the point here, but having worked on large code bases >> with a wide variety contributors before, I find it very advantageous >> that programmers are prevented from writing an amount function whose >> behaviour depends on command line arguments without at least an >> indication in the type. The fact that the function can not perform >> stuff like that is precisely the guarantee that the Haskell type gives >> me... > > I don't think there's an answer that's uniformly right; it depends on > whether you think of the input to the program, e.g. the environment, > command-line arguments, etc. as 'constant' and in some sense, pure. > The latter are constant in the sense that they never change, but they > are not fixed at compile-time. Other languages effectively treat them > as pure (by passing them directly to main), whereas Haskell chooses > not to, which is probably the reason why getArgs has IO in its type > (something that seems unintuitive at first.) > > That precedent supports the view that e.g. a command-line flag > shouldn't affect behavior without the type reflecting it, e.g. by > doing IO, but the de facto use of the unsafe IO trick means not > everyone agrees. > _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Dominique Devriese-2
In practice, the amount of time you have to spend testing each function, to make sure its IO doesn't trip up in some corner case, is usually greater than the amount of time a rewrite-for-IO would take. Tom On Jun 28, 2012 2:54 PM, "Dominique Devriese" <[hidden email]> wrote:
2012/6/27 Tillmann Rendel <[hidden email]>: _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Free forum by Nabble | Edit this page |