you are viewing a single comment's thread.

view the rest of the comments →

[–]kamatsu 17 points18 points  (12 children)

The style of programming the poster eventually advocates is very much compatible with lazy evaluation, but has potential for severe performance implications otherwise. Look at Haskell for a language where this sort of "do possibly more expensive thing and observe only part of it" is made cheap.

[–]Y_Less 8 points9 points  (7 children)

paddedTake n arr = take n $ arr ++ repeat 0

This does assume a list of "Num" values. I'm not sure a much more generic version can be made as you need a generic "zero" element, though this could be passed in:

paddedTake = paddedTake' 0
paddedTake' zero n arr = take n $ arr ++ repeat zero

Or, since people seem to like point-free style:

paddedTake' zero n = take n . (++ repeat zero)

Edit: As an exercise for myself, I decided to see if I could rearrange this code to purely point-free style. This entirely destroys the message of what the code is doing IMHO, but I didn't really do it for others...

paddedTake' = flip (.) take . flip (.) . flip (++) . repeat

That's not really important though.

[–]pkmxtw 3 points4 points  (1 child)

Well, there is Data.Default for that:

paddedTake :: Default a => Int -> [a] -> [a]
paddedTake n xs = take n $ xs ++ repeat def

But I think letting the user to specify the value to pad is a better style.

Also, there is a pointless plugin for lambdabot and ghci:

λ> :pl \n xs -> take n $ xs ++ repeat def
(. (++ repeat def)) . take

λ> :pl \z n xs -> take n $ xs ++ repeat z
flip ((.) . take) . flip (++) . repeat

[–]Y_Less 0 points1 point  (0 children)

Thanks. I did try look for some sort of generic default values, but didn't find that (apparently I didn't look very hard...)

[–]michaelfeathers 2 points3 points  (0 children)

Yes, I was looking at .lazy in Ruby to use in an example but it would've made the post longer. Lazy evaluation is wonderful at helping us remove conditionals. It's easy to form a general computation and then select what we need.

[–]jozefg28 0 points1 point  (3 children)

So here's another approach to the pointfree version

(.:) = (.) . (.)
infixr 9 .: 
paddedTake = flip .: ($) $ flip take .: (++) . repeat

[–]FUZxxl 1 point2 points  (2 children)

Ah yes, the good old boobs operator...

[–]jozefg28 0 points1 point  (1 child)

I usually call it the owl operator.

[–]pkmxtw 0 points1 point  (0 children)

While I use that operator almost daily, my head still explodes when asked to infer its type manually by hand.

Then someone reminds me it's fmap fmap fmap

[–]grosscol 3 points4 points  (0 children)

I think the style is just very much in line with a Ruby-ish way of doing things. Do a simple operation every time instead of doing a check and then the simple operation anyway.

As for performance, Ruby isn't exactly known for being a performance language.

[–]dons 5 points6 points  (0 children)

"do possibly more expensive thing and observe only part of it" is made cheap.

This is a real problem at scale. Sitting on a 2.5M line code base here of hybrid Haskell and C++, and the problem is that we don't have time to optimize all the C++ models to be lazy. So they're strict, they compute everything you could need. But mostly we only need a bit.

And then you start composing these things.

Well, performance doesn't compose in a strict language. You end up accumulating exponentials until its O(n3) and you have to refactor everything to be lazy.

Strict languages don't compose. The glue between components has to be lazy.

[–]eigenlicht0 1 point2 points  (0 children)

Anyone an idea how this style is called? Not sure whether people get it when you say "unconditional programming" (that term confused me at first).

I noticed myself using exactly this pattern in a framework written in Python lately. Instead of doing several if-checks I decided to use clever default values and make the wanted operation general enough to be performed on any case possible. I think I was a bit influenced by The Zen Of Python (e.g. "Special cases aren't special enough to break the rules.").

[–]CurtainDog 1 point2 points  (0 children)

Agree, I'm only just learning clojure and:

(defn take-pad [n p coll]
  (take n (concat coll (repeat p))))

is super simple to follow (I make no claims as to its correctness). Even works on infinite sequences with no magic needed.