**Direct link to multi-page HTML slides**
*Configuration Without the Bugs and Gymnastics*
http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/index.html
*Logging Without Side-effects*
http://dl.dropbox.com/u/7810909/writer-monad/chunk-html/index.html
*Configuration Without the Bugs and Gymnastics [main method]*
- Scala http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/apb.html
- Haskell http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/apc.html
- C# http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/apd.html
**Archive of the slides in all available formats (PDF, HTML, Multi-page
HTML, PNG)**
*Configuration Without the Bugs and Gymnastics*
http://files.meetup.com/1443989/reader-monad.tar.gz
*Logging Without Side-effects*
http://files.meetup.com/1443989/writer-monad.tar.gz
PS: There may be video available in the future since I saw there was a
camera. I'll wait and see I suppose.
--
Tony Morris
http://tmorris.net/
*Configuration Without the Bugs and Gymnastics*
http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/index.html
--
Tony Morris
http://tmorris.net/
Note that Reader=Function1 and map=compose. The flatMap method is
missing on Function1.
I do a fair bit of Lisp programming (Clojure, Racket, Common Lisp) and
I use macros for logging and I can control when and what those macros
expand to in terms of logging. When I turn off logging the macros
literally expand to nothing at compile time - *poof* logging is gone.
The solution you are promoting would require me to leave in the
artifacts about logging but just make the writer monad perform no-ops
when called (and presumably the JIT optimizes this out, maybe), but I
really and truly do not want logging artifacts to alter the signature
of code, and I can really see no good reason why I should want it to
be otherwise. I've even attached via slime to running processes and
replaced functions live after adding logging for special purpose
debugging or, more commonly, timing, but then never actually
committing the ad-hoc logging to the original code. There are
numerous cases where logging is elective or of short-term value where
one should not feel compelled to alter the signature of functions to
wrap them in some new container merely for the purposes of supporting
logging. Imagine, for example, that I use version X of a third-party
library that contained no logging code but version X+<small increment>
added logging and is therefore API incompatible with my existing code
base. Now imaging this to be the case for a dozen or more libraries,
each potentially implementing their own WriterMonad some of them
taking Scala lists or some-such or Java ArrayLists or hybrid solutions
that take both or yet another thingy here-to-fore not yet mentioned.
Or another option where we have something in Java wrapping some native
library.
I certainly agree that logging can get pretty messy, kinda. But my
actual experience simply has not borne out logging catastrophes.
How much do you already know about monads and applicative functors?
That's funny, because that's how I feel about the proposed approach.
The problem of logging, correctly stated, is: the ability to record
artifacts of program execution in such a way as to not alter the
outward appearance (interface, signature, call structure) to any
significant degree. Logging can be introduced through many
mechanisms: explicit introduction by the programmer, implicit logging
as a result of telling the run-time to log certain function
invocations (think of what some profilers do by injecting profile
counters into code - these are also examples of logging), or as a
result of deliberate modifications to the running program to add
logging capabilities.
> I don't know where to begin
> countering your example, since I don't know how much you already do
> understand. Perhaps I'll just start right at the beginning so that I can
> ensure that all other possible leakages are dealt with. On the other
> hand, maybe all that invested effort will not achieve its goal anyway.
> This is my dilemma. It is a risk. I'll see what "I can do.
>
> How much do you already know about monads and applicative functors?
A reasonable amount. I have made my way through the McBride and
Paterson paper "Applicative programming with effects", good stuff,
but, honestly, it sounds like you're going in the direction of one
needing a PhD in computer science (informatics) to add logging to
one's code, I would suggest you don't go down that road. There is
really no need for debate, the issue, in both my mind and experience
has a binary answer: can you envision a method for introducing and
removing logging that meets the criteria I outlined above in a purely
functional way? I'm quite sure the answer is no. The proposed
approach is an interesting model of logging, but it is only a model,
and it is incomplete. The typical responses from the FP community is
to try to rephrase the debate, which has been very much less than
satisfying. I will wait for your responses on this subject and
consider them seriously.
--
Jim Powers
I can and do understand *why* FP works the way it does and if I follow
the FP way of life there are a number of benefits I can reap. Then
again, I also understand that in the end that I'm responsible for what
I write and conforming to a particular way of viewing the world for
the sake of conforming is hardly what people who pay me to write code
are interested in. If I need to inject some timing logging because I
suspect something is taking longer than it should it should take no
longer than the time it takes to type the logging statement to get the
job done, ever.
--
Jim Powers
Honestly mate, I'm struggling to take your points seriously. I know you
have good intentions though.
Sadly, I'll guess we'll have to chalk this up to living in different universes.
> You will also know that changing the type signature doesn't affect
> clients who are abstracted on that type signature, or if they aren't,
> then it's important for them to raise a type-error.
I've introduced logging where there wasn't and removed it where there
was in both cases I came away unharmed and the code that needed to
interface with the code where the logging modifications were made was
none-the-wiser. The compilers didn't complain in either case (as
desired and intended) but the information I needed about the execution
of some blob of code was extracted. Job done, QED.
I'm certain that our views on this subject stem from the fact that I
was using a Lisp in these cases and logging was meta-programming and
not programming. Hence my disagreement about your premise on logging:
I was using code to emit different code depending on my logging needs.
Often, I was not even modifying the underlying code at all (using
before, after, and around methods from CLOS) to get the logging I
wanted. Effectively I was telling the runtime to alter it's view of
select functions and substitute my functions in their place,
eventually (in the case of around methods) I would call the underlying
function. This was done deliberately in such a way as to not alter
the signature of the underlying functions, a number of which I didn't
even have source code for.
Your claim is that this behavior should introduce a type error, I
claim that that claim is false. That claim is only true in lesser
computation models where the kinds of alterations I have been
describing are prohibited by some paternal force. So far the kinds of
responses I've gotten have fallen in-line with others of an
FP-religion bent: follow the rules of the high priesthood of FP and
all will be milk and honey. It's not that Monads or Applicative
functors have a distorted view of the world it's me and my shabby ilk
and I should know better.
Applicative functors: do absolutely nothing to address the issues I've
raised, my complaints remain unaltered. At best it seems like some
nifty set of tricks that might be useful to a compiler writer for
representing threaded state in an environment, generally something
that should be completely transparent to the programmer.
>> but, honestly, it sounds like you're going in the direction of one
>> needing a PhD in computer science (informatics)
> WTF?
Seriously? I'm talking about an act every programmer who as ever
programmed has done at least once: added logging to a program, even if
that means print statements and your response is to ask what I know
about Applicative Functors before continuing the conversation. I may
be crazy but that kind of exchange seemed ... odd.
--
Jim Powers
It can go wrong, things can always go wrong. By changing a pure
function to one with embedded side-effects one can alter the
quasi-guarantees the compiler can make given pure functions. So, for
instance, logging code can cause things like OI exceptions to occur.
In practice this outcome is almost always hardly worth considering
compared to the benefit of the embedded logging code. Then again,
most type systems cannot even express things like concepts like
Natural numbers greater than zero[1] so anything with a x/y can blow
up on you as well. The approach proposed, while interesting from an
academic (or pedantic, depending on one's views) point of view appears
to be far too simplistic for anything but toy applications.
Another problem with introducing side-effectual code is it makes it
hard to impossible for the compiler to do expression rewriting. I'm
sure that this happens to some extent in the Scala compiler, but GHC
is a mind-bending work of art when it comes to applying this kind of
trick to optimizing code. For instance, in GHC you can use a "back
door" called unsafeIO to create a mini IO monad that would allow you
to do things like traditional logging, but the interesting thing is
depending on what the GHC compiler does you may never see your logging
code called because GHC has literally optimized the surrounding code
(and associated code paths) out of existence, or caused the logging to
occur in different contexts because the compielr has moved the code in
question around.
--
Jim Powers
[1] Sadly, a relatively neglected language called ATS
(http://www.ats-lang.org/) incorporates much of the same magical
Algebraic type capability as systems like Haskell but is a fully
imperative language with the ability to embed program proofs directly
into your programs. In ATS it's a trivial matter to declare a type
representing positive integers, or even integers, etc. ATS is
phenomenal because it allows you to write blazingly fast programs (see
http://bit.ly/fj0Blh) with embedded proofs and checks for correctness,
a step beyond mere type checking.
--
Jim Powers
man, you are such a nerd! i love it (i've been following ats for a
while, tho never written anything in it). hey -- i see your ats and
raise you a ddc.
* call counting
* execution timing
* collecting JVM runtime stats
etc...
Are added to your code individually. It's no longer mere list of
strings logging any more. Each one of these would need some sort of
special "pathway" to get the needed "logging infrastructure" into the
body of the code. There have been times (again in programming Lisp)
where I have done any combination of the above without ever even
touching the body of the function in question. In scala one could
piggy-back on something like Spring join points (around methods in
Lisp-speak) to achieve similar results.
--
Jim Powers
--
Jim Powers
I raise you one Stalin (http://community.schemewiki.org/?Stalin) ;-)
--
Jim Powers
word, although i guess that takes us away from dependent typing,
effect typing, et. al. (and i'm a static typing bigot for the most
part, so i'd maybe go for alice ml rip, or maybe mlton and the fun (if
expensive) region inference stuff.) things like concurrent clean
(which supposedly is turning into a Haskell compiler back end any day
now real soon) with the uniqueness typing sounds like fun. although
from what i understand from the ddc thesis paper, all the typing soon
makes you wish you were, in fact, just using dynamically typed scheme.
(tho i wish i had time to get into typed racket.) cal on the jvm would
be fun, too. or bigloo on the jvm. or or or so many shiny toys, so
absolutely no zero nada zilch zippo time to play with them. :-( let
alone learning scala.
sincerely.
I have been using Scala in my day job for about 1.5 years and very
much enjoying it (anything but Java on the JVM ;-) ), although I would
be happier with Clojure ;-)
--
Jim Powers
Well, this is both an ironic and disappointing conclusion to a thread
that started out with links given to presentations clearly intended to
reduce ignorance in the world.
On the subject of my disagreement with your proposal I'm not ignorant
(although that is likely debatable ;-) ), I would characterize my
attitude as a healthy and appropriate skepticism about your claims on
the nature of the problem you're trying to solve. It's not the only
thing I could claim skepticism about with regard to functional
programming, but it's the most concrete one relative to the
presentations you linked to. This is not the first time that I've
debated an FP aficionado on this subject - not one has succeeded,
either by argument, or example, to have properly captured the nature
of this problem, which is *exactly* as I outlined earlier in this
thread. Interestingly though, thinking about this subject more today
I have started the germ of an idea about a set of Lisp macros that
could actually implement your proposal while hiding all evidence of
the chicanery of altering a function's signature. The macro would be
smart enough to know where it could fix-things-up and inject the
logging instrumentation (when logging is turned on, otherwise *do
nothing* when logging is turned off), and stop at functions that are
outside it's control[1]. So I could implement the FP-style logging
system without anyone knowing it (except for the macros involved).
Lisp-style macros for the win, again ;-).
--
Jim Powers
[1] This part of etorreborre post, which intersects fully with my
original criticism, continues to be unaddressed.
The reason I asked if you understood certain concepts had nothing to do
with "requiring a PhD" but because I contend that nobody on this planet
has both this understanding and simultaneously raises the incredibly
weak points that you have done.
If you're looking to raise legitimate objections to this concept, then I
can help you with that. They do exist. You didn't raise any points that
are worth discussing, which is why I have chosen not to spend any effort
on it. You've completely misunderstood and the prognosis for remedy is
poor. I hope you understand this -- it's not a objection to be taken
personally -- but simply one of efficiency.
You may dabble in your perception that there is a "win", but under my
value system, ignorance is never a win. I also don't waste time on
disappointment.
Hope it works out for you.
--
Jim Powers
A common misunderstanding of Murphy's Law. The only things that might go
wrong are the things that can possibly go wrong. That may sound
tautological, but the point is you can design failure modes out. Cars
with fuel injection cannot have carburetion problems, e.g. (They can
have problems that carbureted engines cannot, of course.)
Languages without side effects cannot exhibits the whole class of bugs
that come from values changing between the time they were calculated
and the time they were inspected or used. It might be technically
correct to say _programs_ without side effect cannot exhibit such
failures, but that is exactly the difference that Murphy's Law goes to.
If the language admits side effects, we cannot know any given program
is free from them and _then_ "things can go wrong."
Randall Schulz
* The "nolog" function is the unital operation for Writer: A =>
Writer[W, A]. That is, Writer[W, _] is a pointed functor witnessed by
the nolong function.
* The "sequence" function (see Applicative Programming With Effects) can
be specialised to:
Traversable[F] => Applicative[M] => F[WriterT[M, W, A]] => T =>
F[WriterT[M, W, A]]
for
case class WriterT[M[_], W, A](a: M[WriterT[W, A]])
This function may turn out to be pretty useful and might even render
many popularly-held misunderstandings moot.
If the notion of automatically lifting a pointed functor truly
fascinates you, maybe take a look at Disciple (DDC), but I think
understanding some of the basics is more useful to continuing this
discussion in any meaningful manner. I've given up on that though, and
therefore, any meaningful discussion. YMMV. Godspeed.
On Thursday March 3 2011, Jim Powers wrote:
> ...
>
> It can go wrong, things can always go wrong. ...
A common misunderstanding of Murphy's Law. The only things that might go
wrong are the things that can possibly go wrong. That may sound
tautological, but the point is you can design failure modes out. Cars
with fuel injection cannot have carburetion problems, e.g. (They can
have problems that carbureted engines cannot, of course.)
Languages without side effects cannot exhibits the whole class of bugs
that come from values changing between the time they were calculated
and the time they were inspected or used. It might be technically
correct to say _programs_ without side effect cannot exhibit such
failures, but that is exactly the difference that Murphy's Law goes to.
If the language admits side effects, we cannot know any given program
is free from them and _then_ "things can go wrong."
Randall Schulz
I was not quoting or referring to Murphy's law, I was merely referring
to a conclusion from experience, but Murphy's law will do.
> That may sound
> tautological, but the point is you can design failure modes out. Cars
> with fuel injection cannot have carburetion problems, e.g. (They can
> have problems that carbureted engines cannot, of course.)
>
> Languages without side effects cannot exhibits the whole class of bugs
> that come from values changing between the time they were calculated
> and the time they were inspected or used.
Strictly speaking treating values as values (immutable, persistent)
does not necessarily follow as a result of the language being
"side-effect-free" or not. Take Clojure's persistent data structures,
they exhibit this property even though Clojure (or Scala, or Java, or
anything else that can use Clojure's persistent data structures) is
not strictly a side-effect-free language.
> It might be technically
> correct to say _programs_ without side effect cannot exhibit such
> failures, but that is exactly the difference that Murphy's Law goes to.
> If the language admits side effects, we cannot know any given program
> is free from them and _then_ "things can go wrong."
OK, let's play that game for a bit. All programs run on machines with
limited resources: stack, memory, disk, IO, etc. I could write
(apparently) perfectly "side-effect-free" code that inadvertently runs
out of RAM or stack, or, I could choose a side-effecting approach
where it is known that RAM or Stack will never exceed X regardless of
the size of input. The former approach may periodically fail where
the latter won't. Then again, the former may be more concise and
expressive, as well as easier to invoke from multiple threads, where
the latter might be convoluted and be a beast to make multi-thread
safe (assuming that multiple invocations from different threads share
state). I get the picture, despite accusations to the contrary, but
either approach is valid for certain applications.
Further, the notion of "side-effect-free" is an illusion, a useful
one, but an illusion none-the-less. All code, once executing, create
side-effects all the time: stack, memory, IO, CPU registers, cache,
etc. What is meant by "side-effect-free" is that the side-effects
incurred by the actual execution of the program follow a pattern with
certain desirable properties, occasionally, with other undesirable
properties. Don't limit the notion of "failure modes" to mere
exceptions. I've observed first hand (in Scala) where pretty
side-effect free code exhibited good run-time performance but then
would stack overflow on certain inputs unless certain values were
computed lazily. Now the code works for all (interesting) inputs but
dropped considerably in performance. In the end I had to replace the
side-effect-free code with imperative code to meet both the
operational and time performance needs (the imperative code ran faster
than both other versions by several orders of magnitude).
--
Jim Powers
short of bugs in the compiler or runtime, cough cough. *that's* what
murphy means, i think :-)
I've observed first hand (in Scala) where prettyside-effect free code exhibited good run-time performance but then
would stack overflow on certain inputs unless certain values were
computed lazily. Now the code works for all (interesting) inputs but
dropped considerably in performance. In the end I had to replace the
side-effect-free code with imperative code to meet both the
operational and time performance needs (the imperative code ran faster
than both other versions by several orders of magnitude).
--
Jim Powers
Specifically Nermin Serifovic talk on performance. Clearly, this just
the tip of the iceberg of course.
--
Jim Powers
Whatever you say.
--
Jim Powers
Side-effect free code is not an illusion.
This is not a "more complicated approach." Quite the contrary -- it's
actually simpler, but for reasons I didn't get in to (they are quite
deep and distract from the point). As some advice, do not conflate "I do
not understand X" with "X is more complicated than what I currently do
understand."
You can increase the number of parameters in the Configuration -- it
won't change anything. I only gave 3 so that the audience doesn't have
to keep more in their head. I don't know what you mean by "toy software."
On 05/03/11 13:08, Russ P. wrote:
> I was bored, so I decided to take a look at some of Tony's slides. The
> "Configuration Withoug Bugs and Gymnastics" slide set caught my
> attention first. On the second or third slide, we see this:
>
> * Let's put the configuration in a once-set widely-scoped variable
> and all functions requiring access to it, automatically do so.
>
> * But this leads to bugs and code that is difficult to read and
> maintain.[1]
>
> * Worse still, we will want to adjust the configuration parameters
> during the application's run � uh oh, we're in a tangle now.
Hello,
Here are some slides from a talk at Brisbane Functional Programming
Group on Monday night (28 Feb) using Scala. This includes a fix for the
bug that was pointed out during the talk and a main method that uses the
reader monad that was omitted for time brevity.
**Direct link to multi-page HTML slides**
*Configuration Without the Bugs and Gymnastics*
http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/index.html
*Logging Without Side-effects*
http://dl.dropbox.com/u/7810909/writer-monad/chunk-html/index.html
*Configuration Without the Bugs and Gymnastics [main method]*
- Scala http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/apb.html
- Haskell http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/apc.html
**Archive of the slides in all available formats (PDF, HTML, Multi-page
HTML, PNG)**
*Configuration Without the Bugs and Gymnastics*
http://files.meetup.com/1443989/reader-monad.tar.gz
*Logging Without Side-effects*
http://files.meetup.com/1443989/writer-monad.tar.gz
PS: There may be video available in the future since I saw there was a
camera. I'll wait and see I suppose.
--
Tony Morris
http://tmorris.net/
On 05/03/11 15:10, Meredith Gregory wrote:
> Dear Tony,
>
> i like this as an introduction to monoids and monads. Since my guess that
> this is your principal aim, i wonder why you didn't make a connection
> between monoid and monad (maybe this is the next installment?).
It's not really. Rather, it's trying to appeal to a problem that I
expect most programmers have encountered before, even expecting them to
have attempted to have tried solving it, then propose a solution that
actually solves it. That monads and monoids actually help solve the
problem is just a consequence of achieving the goal, not actually part
of the goal.
> Getting
> people to understand that monads are a monoid object in the category of
> endomorphisms is really useful. Once you see that a monoid is really just a
> monoid "up one level" life becomes easier. It's usually easy to get folks to
> see how familiar collection monads look like monoids. List[A] is the free
> monoid (with the inhabitants of A as the generators). Set[A] is the
> monoid (with the inhabitants of A as the generators) satisfying idempotency
> of composition, etc.
>
> i was distracted by the use of logging as an example to illustrate these
> core ideas, however because the type analysis of logging misses a crucial
> point about logging. i expect my logs to persist from run to run. The
> monoid-based type analysis misses that. As a first-cut abstraction it's ok;
> but, logs that don't persist from run to run don't have a lot utility for me
> because sometimes i'm using logging to track down a session-crashing
> problem.
Yes, this kind of contention is often a problem. Let's just reflect for
a moment -- some of the (noisiest) comments put forward on the
[scala-user] mailing list didn't even get off first-base and so there
was a big distraction of not-particularly-interesting points expecting
to have been taken seriously on the level of the discussion topic,
rather than expending effort on understanding the underlying data
structures and type system theory involved (which I am certain is the
only constructive way to truly satisfy the concern without telling lies!).
How could I have possibly moved on to more meaningful issues in light of
this? Choosing the next step is always a tough balancing act and I don't
always have the right answer. Nevertheless, the overall feedback that I
have received including on other channels and after delivering the
presentation itself indicate that I have hit exactly the right point of
balance. I don't aim to appease the expectations of everyone -- it's
just unachievable -- rather, I hope to help the most curious and those
willing to expend the effort for themselves. At the moment, I haven't
been shown that I couldn't have achieved this any better by doing
anything differently. I do like to aim high though.
I had 30 minutes for each of these talks by the way. I aim for strict
punctuality in presentations.
> i do understand how to solve this problem! And, your framework extends
> smoothly to this variation. It was just distracting because there's a lot of
> rich stuff being swept under the rug. Of course, i recognize that this sort
> of question might be just the sort of thing you are attempting to provoke to
> set up the opportunity to show how that variation fits the framework in
> realtime during the talk.
Another 30 minute talk itself? :) Yeah good point, but we have to cover
the basics first -- or sometimes be distracted from the basics as it
were for one or two cases.
> As an aside -- but somewhat related -- i've taken to doing all logging as
> XML. Then i use a poor man's way of probing the log for properties. By
> Curry-Howard, essentially, an XML schema (doesn't matter XSD, RelaxNG, DTD,
> don't care) is a *property* of a log file. Checking the log for conformance
> to the schema corresponds to determining whether the log has the property.
> For example, you can check whether an A request is always matched by a B
> response, etc. This is particularly useful when trying to do basic debugging
> by log inspection.
It might be worth noting that the Writer data structure is useful for
logging, not necessarily debugging.
As a side-note, I once covered your concern (i.e. program crash) in an
explanation of the WriterT monad transformer that I once gave using Scala:
In response to "How to add tracing within a for-comprehension?"
http://stackoverflow.com/questions/2322783/how-to-add-tracing-within-a-for-comprehension/4871353#4871353
(see sub-heading critique)
--
Tony Morris
http://tmorris.net/
Russ,
Try writing a library that allows different configurations. Your
technique will not work, since the application code will have static
references to your configuration parameters. The point is to abstract on
the configuration. You raise the issue of having a "default
configuration." This is actually a good example of where you will fail
with this technique, since you cannot provide part of the configuration
and require the user to pass the rest.
This is not a "more complicated approach." Quite the contrary -- it's
actually simpler, but for reasons I didn't get in to (they are quite
deep and distract from the point). As some advice, do not conflate "I do
not understand X" with "X is more complicated than what I currently do
understand."
You can increase the number of parameters in the Configuration -- it
won't change anything. I only gave 3 so that the audience doesn't have
to keep more in their head. I don't know what you mean by "toy software."
On 05/03/11 13:08, Russ P. wrote:
> I was bored, so I decided to take a look at some of Tony's slides. The
> "Configuration Withoug Bugs and Gymnastics" slide set caught my
> attention first. On the second or third slide, we see this:
>
> * Let's put the configuration in a once-set widely-scoped variable
> and all functions requiring access to it, automatically do so.
>
> * But this leads to bugs and code that is difficult to read and
> maintain.[1]
>
> * Worse still, we will want to adjust the configuration parameters
> during the application's run — uh oh, we're in a tangle now.
Function1 already has map, except it is called compose/andThen and so
cannot be called with a for-comprehension*. The flatMap method is simply
missing (so are many other methods such as the applicative functor (S
combinator) and even the 'on' method recently discussed). There is also
a contention in that the bytecode for these methods would be included
for every function literal, blowing out the bytecode size unnecessarily.
I'm don't understand what the existence or non-existence of these two
methods in the standard library has to do with this point being more
relevant.
* The map function has this structure:
F[A] => (A => B) => F[B]
For example, let F=List, then we have:
List[A] => (A => B) => List[B]
Now let F=Function1[T, _], then we have:
Function1[T, A] => (A => B) => Function1[T, B]
This is the compose method. It's also a once-inhabited type.
On 05/03/11 15:49, Russ Paielli wrote:It doesn't belong in the standard Scala library, except to the extent
> And if you can get your
> implementation into the standard Scala library, your point will be more
> relevant.
that map and flatMap belong on Function1. That's what the reader monad is.
Function1 already has map, except it is called compose/andThen and so
cannot be called with a for-comprehension*. The flatMap method is simply
missing (so are many other methods such as the applicative functor (S
combinator) and even the 'on' method recently discussed). There is also
a contention in that the bytecode for these methods would be included
for every function literal, blowing out the bytecode size unnecessarily.
I'm don't understand what the existence or non-existence of these two
methods in the standard library has to do with this point being more
relevant.
PS: I truly don't understand what it is with [scala-user] and the
compulsion to say incredibly stupid things.
Russ,
Try writing a library that allows different configurations. Your
technique will not work, since the application code will have static
references to your configuration parameters. The point is to abstract on
the configuration. You raise the issue of having a "default
configuration." This is actually a good example of where you will fail
with this technique, since you cannot provide part of the configuration
and require the user to pass the rest.
This is not a "more complicated approach." Quite the contrary -- it's
actually simpler, but for reasons I didn't get in to (they are quite
deep and distract from the point). As some advice, do not conflate "I do
not understand X" with "X is more complicated than what I currently do
understand."
You can increase the number of parameters in the Configuration -- it
won't change anything. I only gave 3 so that the audience doesn't have
to keep more in their head. I don't know what you mean by "toy software."
On 05/03/11 13:08, Russ P. wrote:
> I was bored, so I decided to take a look at some of Tony's slides. The
> "Configuration Withoug Bugs and Gymnastics" slide set caught my
> attention first. On the second or third slide, we see this:
>
> * Let's put the configuration in a once-set widely-scoped variable
> and all functions requiring access to it, automatically do so.
>
> * But this leads to bugs and code that is difficult to read and
> maintain.[1]
>
> * Worse still, we will want to adjust the configuration parameters
> during the application's run — uh oh, we're in a tangle now.
>>> during the application's run � uh oh, we're in a tangle now.
>>>> *Configuration Without the Bugs and Gymnastics*
>> http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/index.html
>>>> *Logging Without Side-effects*
>> http://dl.dropbox.com/u/7810909/writer-monad/chunk-html/index.html
>>>> *Configuration Without the Bugs and Gymnastics [main method]*
>>>> - Scalahttp://dl.dropbox.com/u/7810909/reader-monad/chunk-html/apb.html
>>>> - Haskellhttp://
>> dl.dropbox.com/u/7810909/reader-monad/chunk-html/apc.html
>>>> - C#http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/apd.html
>>>>
>>>> **Archive of the slides in all available formats (PDF, HTML, Multi-page
>>>> HTML, PNG)**
>>>>
>>>> *Configuration Without the Bugs and Gymnastics*
>> http://files.meetup.com/1443989/reader-monad.tar.gz
>>>> *Logging Without Side-effects*
>> http://files.meetup.com/1443989/writer-monad.tar.gz
>>>> PS: There may be video available in the future since I saw there was a
>>>> camera. I'll wait and see I suppose.
>>>>
>>>> --
>>>> Tony Morrishttp://tmorris.net/
>>
>> --
>> Tony Morris
>> http://tmorris.net/
>>
>>
>>
>
--
Tony Morris
http://tmorris.net/
This "solution" is covered in the presentation.
[1] As is usual for widely-scoped, write-at-least-once values"
-------------------------------------------------------------------------
That's nothing more than an unsubstantiated assertion. Actually, I have no doubt that "widely-scoped, write-at-least once values" can lead to bugs if they are misused, but it does not necessarily follow that they cannot be used properly. For example, if they are used with wildcard imports, that could severely degrade readability. However, when I see Config.useTrackFilter or Config.VertAccel in my code, I know immediately where it came from and what is going on. The maintainability and readability of "Config.parameter" is the least of my concerns.
--Russ P.
>>> during the application's run — uh oh, we're in a tangle now.
On 06/03/11 08:48, Russ Paielli wrote:
> On Sat, Mar 5, 2011 at 1:57 PM, Tony Morris <tonym...@gmail.com> wrote:
>
>> This "solution" is covered in the presentation.
>>
>>
> Yeah, right. This is the coverage:
>
> ---------------------------------------------------------------------
> "--But this leads to bugs and code that is difficult to read and
> maintain.[1<http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/ar01s02s02.html#ftn.id522102>
> ]
>
> [1<http://dl.dropbox.com/u/7810909/reader-monad/chunk-html/ar01s02s02.html#id522102>]
>>>>> during the application's run � uh oh, we're in a tangle now.
--
Tony Morris
http://tmorris.net/
>>>>>> during the application's run — uh oh, we're in a tangle now.
I can see why you might not be sold on Tony's approach, but surely you
see that having mutable shared global state is a recipe for a lot of
extremely painful headaches? I can only imagine that you'd only ever
suggest keeping your config as a global set of vars if you've never
had the experience of maintaining legacy code from back when people
thought this was a good idea.
I would say basically never.
> I'd say it is appropriate for initializing global configuration parameters that are not
> supposed to change after initialization. And if you use vals, you can ensure that they
> *won't* change after initialization.
I'm pretty sure that the message is not about the mutability (or lack
thereof) of "configuration" parameters. Even if you choose to keep
your configuration object there is no *good* justification to tie
things that use values from that configuration by explicitly
connecting the consumers of the configuration to the (single) instance
of the supplier of that configuration. In fact there are lots of good
reason *not* to do what you are doing.
> It will require recompiling to change
> parameters, but that is acceptable in my particular case. My configuration
> parameters are typically mode switches or default values (e.g., the vertical
> acceleration of a commercial transport aircraft when it starts an altitude
> transition).
>
> If you're developing a library, and the client needs to set some or all of
> the parameters, then you have a couple of options: (1) You can either read
> the parameters at run time from a file, from user input, or from some other
> input device, or (2) you can use vars instead of vals. Off hand, I can't
> think of another option. If there is one, please tell me.
As an example, imagine that, at some point, you want to create
multiple simultaneous instances of the systems configured through the
configuration approach you're using. If you cannot imagine doing that
yourself then try to imagine the possibility that somebody else would.
Given your approach it would be impossible - making the reasonable
impossible is typically not in your best interest.
> Unless I am missing somthing (which is entirely possible), any other
> "boilerplate" for initializing configuration parameters is just that,
> boilerplate for implementing one of the two options llsted above.
The "boilerplate" you're referring to really isn't that at all: you
have a system that needs configuration, therefore you need to
configure it to use it. How you acquire a configuration is a
completely independent problem from the use of that configuration.
Solve the problems independently and then stitch together the
solution. Imagine multiple pathways to acquiring a configuration.
The details of how you internally represent that configuration is
almost certainly of no concern to the system using that configuration
so as long as the system gets the configuration it wants in the form
it wants. Why choose a concrete implementation and limit the use of
your system when a well chosen abstraction will enable a number of
interesting applications of the code your writing?
--
Jim Powers
I'm pretty sure that the message is not about the mutability (or lack
thereof) of "configuration" parameters. Even if you choose to keep
your configuration object there is no *good* justification to tie
things that use values from that configuration by explicitly
connecting the consumers of the configuration to the (single) instance
of the supplier of that configuration. In fact there are lots of good
reason *not* to do what you are doing.
As an example, imagine that, at some point, you want to create
> It will require recompiling to change
> parameters, but that is acceptable in my particular case. My configuration
> parameters are typically mode switches or default values (e.g., the vertical
> acceleration of a commercial transport aircraft when it starts an altitude
> transition).
>
> If you're developing a library, and the client needs to set some or all of
> the parameters, then you have a couple of options: (1) You can either read
> the parameters at run time from a file, from user input, or from some other
> input device, or (2) you can use vars instead of vals. Off hand, I can't
> think of another option. If there is one, please tell me.
multiple simultaneous instances of the systems configured through the
configuration approach you're using. If you cannot imagine doing that
yourself then try to imagine the possibility that somebody else would.
Given your approach it would be impossible - making the reasonable
impossible is typically not in your best interest.
Understood. In the end the trade-offs are yours to decide. Using symbolic constants clearly is superior to hard-coding values. If these parameters are long-term constants then I concede the point. If these were like per-run configured inputs then I would continue to urge you to reconsider.
--
Jim Powers
To give you a Scala-specific answer, then yes, occasionally I see some
"imperative" nasties going in to the language and I think there is
chronic under-estimation of the detrimental impact of these. One example
is an implicit function in Predef of the type A => Unit. These are very
serious issues in my opinion and probably others who place high value on
the correctness of their software.
To summarise, I am not sure what you are asking exactly or feel
confident in predicting the motivations for your questions.
> Tony, you know that I don't take what you say as gospel truth, but I do
> place some significant value on your opinion. You seem to be an FP purist,
> and I'd be interested in your opinion on this matter. Here's what I am
> trying to figure out: are the non-functional aspects of Scala just a
> concession to bad programmers and sloppy programming practices, or are they
> truly the best alternative in some cases?
I am not a "FP purist." You might think I am simply nit-picking here or
rejecting labels because they hurt my feelings. They don't. Rather, I
wish to correct you here because by believing in this artificial
"delineation" gives rise to all sorts of other misunderstandings. I
value that which is real and you probably do too. As a result, I don't
think your subsequent question makes much sense.
I want my software to work correctly, today and tomorrow, and I want to
get it done fast and have the capability of continuing to get it done
fast (and still correct). Nothing more, really really nothing more than
this. Everything else is a consequence of this objective, truly everything.
> --Russ P.
>
I hope this clears a few things up for you.
However, your question is specific to "programming techniques", which is
a slightly different question (possibly even more interesting).
Nevertheless, I think answering it comprehensively and usefully is quite
a long essay, even significantly longer than the one above.
In short, the answer is yes. I hope this is satisfactory for now.
PS: No I don't have a problem with criticising Scala. Like I said, I'm
interested in producing software with specific objectives, not worrying
about others' possibly delicate feelings, which I dismiss when there is
a contention.
--
Tony Morris
http://tmorris.net/
Maybe this explanation will help.
http://programmers.stackexchange.com/questions/51245/what-kind-of-things-are-easy-in-haskell-and-hard-in-scala-and-vice-versa/51333#51333
However, your question is specific to "programming techniques", which is
a slightly different question (possibly even more interesting).
Nevertheless, I think answering it comprehensively and usefully is quite
a long essay, even significantly longer than the one above.
In short, the answer is yes. I hope this is satisfactory for now.
:-)
Sure, but it can be dangerous if some precautions are not taken... ;-)
--
Philippe Lhoste
-- (near) Paris -- France
-- http://Phi.Lho.free.fr
-- -- -- -- -- -- -- -- -- -- -- -- -- --