|
|
"Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message
news:2klar7F3j1q9U1@uni-berlin.de...
> "I spy" <s_nedunuri@yahoo.com> escreveu na mensagem
> news:2kj6veF31kbuU1@uni-berlin.de...
> strings,
the[color=darkred]
>
> [snip]
>
abstraction[color=darkred]
> is
>
> Please don't do that. You picked a problem and was given a solution in a
> FPL. Don't back-pedal and say that the FP solution doesn't work because of
> new requirements. The DIP article is simple and I showed you how can you
> achieve dependency inversion with HOFs.
Sorry, I am not trying to move the goal posts. But if I may stand on my here
soapbox a minute, I am a little tired of seeing "solutions" in someone's
favorite language only to find they don't scale. And by scalability I mean
resilience in the face of growth of the problem to be solved. Otherwise its
really of no interest to me. The other thing I'm loooking for is
resilience in the face of change. That is I need to know the solution is
maintainable. Maintenance is a huge problem. There's more $ spent on program
maintenence than on new program development, and thats with the software
explosion going on around us. FP has generally failed to address these. OO
did, perhaps not very well, but it did, and caught on. Without at least
investigating these two
concerns (being extreme to make a point, not directed at your example) we
might as well all be
programming in assembly (which actually has a very simple DIP solution).
Sorry I'll get off now. If you don't like my approach, then I'll hang up.
Otherwise I'm happy to talk. As for your continued example, I appreciate you
taking the time to do this. So let me study it in more detail today and get
back to you
cheers
were[color=darkred]
> ready
but[color=darkred]
> the
their[color=darkred]
> state.
>
> Just like the Button/Lamp example I gave. In FPLs we have closures (i.e.
> functions that carry a state) sharing some state, just like the three
Device
> functions shared the "state" reference. So instead of having two HOF
> parameters we can have four:
>
> copy2 :: MonadPlus m => (a -> m b, m Bool) -> (m (Maybe a), m Bool) ->
m
> ()
> copy2 (output, checkOutput) (input, checkInput) =
> do {inputOk <- checkInput;
> guard inputOk;
> mx <- input;
> case mx of {
> Just x -> do {outputOk <- checkOutput;
> guard outputOk;
> output x;
> copy output input};
> Nothing -> return ()}}
>
> Notice how I packed the check operations together with the respective
> input/output operations, they should belong to a single type/adt, so I can
> define it:
>
> type Input m a = (m (Maybe a), m Bool)
> type Output m a b = (a -> m b, m Bool)
> copy2 :: MonadPlus m => Output m a b -> Input m a -> m ()
>
> And show the change in the signature of copy2. I used a simple type
> definition, but we could use a data declaration, don't export the
> constructors from our module and get a real ADT for each type. But if we
> follow good OOP practices we should first ask ourselves: Why do we need to
> ask for the state of input/output before telling them to do their jobs?
> Isn't it a violation of the "Tell, don't ask!" principle? What I'm trying
to
> say is that in all years I spent working with Java I would rarely have a
> method sending more than a couple of different messages to another object.
>
> can't
>
> Of course you can (but not in Haskell). In languages of the ML family
(e.g.
> SML, O'Caml) modules are first class citizens and you can have
parametrized
> modules, but they're called functors. Here's an O'Caml example (courtesy
of
> Neel Krishnaswami) using higher-order modules, functors and the object
> system to extend a ref-like object (i.e. something with get and set
methods)
> to have undo facilities:
>
> (*A simple module with no parameters*)
> module type REF =
> sig
> (*Defines the type of a ref-like class. If could have a type parameter
> instead of a hard-coded string type but it isn't important in this
> example.*)
> class t :
> object
> method get : string
> method set : string -> unit
> end
> end
>
> (*This module is a functor. It can take parameters, including other
modules
> as parameters.*)
> module Add_Undo(Base : REF) =
> struct
> (*Defines a subclass of class parameter. It's similar to the C++ templates
> for mixins where we have the superclasses as template parameters. This
class
> can be used wherever the super-class can be used and it doesn't depend on
> the implementation of the super-class, just on its interface.*)
> class t =
> object
> inherit Base.t as super
>
> val mutable history = []
>
> method set s =
> begin
> history <- super#get :: history; (*super#get is like
super.get()
> in Java.*)
> super#set s;
> end
>
> method undo =
> match history with
> | [] -> ()
> | x :: xs -> (super#set x; history <- xs)
> end
> end
>
>
>
this?[color=darkred]
>
> [snip]
>
> line
can[color=darkred]
above[color=darkred]
methods[color=darkred]
> and
>
> That's one thing I don't like: theoretical examples from the top of our
> heads. It's an impossible to win situation because no matter how complete
is
> my solution you can always say "But if I change the scenario to this your
> solution would be cumbersome". If you're going to play this game don't
even
> bother answering this post, otherwise we can continue this.
>
> And just to remember something again I am a non-academic Java programmer
> from the industry, so I'm not saying that we could FPL to solve the
> academical OO problems (e.g. collection frameworks, idiotic shape
problems)
> but to solve the n-tiered SQL/HTML problems.
>
> So answering your question let's say that we have a Device with additional
> capabilities, for example it can call a function to tell when it needs
> repair. We can use composition to model this:
>
> data Repairable d = Repairable {thing :: d, onFailure :: String -> IO
> ()}
>
> We could put the switch and printStatus operations on a type-class:
>
> class Operable o where
> switch :: o -> IO ()
> printStatus :: o -> IO ()
>
> Declaring instances for both Device and Repairable Device:
>
> -- The old definitions inside a class
> instance Operable Device where
> -- An utility operation to flip a device
> switch (Device isOn turnOn turnOff) =
> do on <- isOn
> if on
> then turnOff
> else turnOn
> -- An utility operation to check the status of a device
> printStatus (Device isOn _ _) = isOn >>= (putStrLn . ("> " ++) .
> show)
>
> -- Delegate the operations to the device
> instance Operable (Repairable Device) where
> switch (Repairable device _) = switch device
> printStatus (Repairable device _) = printStatus device
>
> Now our server works on operables rather than devices:
>
> server :: Operable a => a -> IO ()
>
> We can make a composition function to extend a device with an additional
> state and callback operation. In this example the extra operation doesn't
> make sense but you get the idea:
>
> makeBreakable failWith super =
> do counter <- newIORef 0
> let updateCounter = modifyIORef counter (+1)
> let shoutIfBroken = do {x <- readIORef counter;
> when (x >= 2)
> (failWith "I'm too old")}
> return $ Repairable {thing = super {turnOn = do {shoutIfBroken;
> turnOn super},
> turnOff = do
{updateCounter;
> shoutIfBroken; turnOff super}},
> onFailure = failWith}
>
> testBreakableLamp = newLightBulb >>= makeBreakable onFailure >>=
server
> where onFailure message = putStrLn ("Fix-me, because " ++ message
> ++"!")
>
> Here's the results I get with Hugs:
>
> Lamp> testBreakableLamp
> status
> switch
> status
> switch
> switch
> switch
> Fix-me, because I'm too old!
> quit
>
> There are two interesting things in this code: makeBreakable extends a
> device with additional state using old-fashioned prototype techniques and
> the Operable type-classe allow different implementations of a similar
> protocol much like Java-interfaces (actually they-re much more powerful
than
> interfaces and this comparison is too simplistic).
>
> We could have another type-class "extending" Operable (not extension
per-se
> but the results are similar in this context) or even make the original
> Device data-type a type-class too. I'm pretty much confident that, despite
> my newbie knowledge of Haskell, I can come up with solutions to any
> extension problems you might think of and they all will be simpler and
more
> concise than the usual class-based OO equivalent. But IME and what other
> people are trying to say here is that in practice one or two HOF do the
job
> most of the times and only in a few cases we need to pack a group of
related
> operations in a single object.
>
> receive
and[color=darkred]
>
> Best regards,
> Daniel Yokomizo.
>
> "Both [G.J. Sussman and D.S. Wise] belonged very much to the LISP
> subculture, neither of the two proved a single theorem, both showed too
much
> and made such heavy use of anthropomorphic terminology that they were
> painful to listen to."
> - Edsger W. Dijkstra
>
>
> ---
> Outgoing mail is certified Virus Free.
> Checked by AVG anti-virus system (http://www.grisoft.com).
> Version: 6.0.713 / Virus Database: 469 - Release Date: 30/6/2004
>
|
|