Pharo-users
By thread
pharo-users@lists.pharo.org
By month
Messages by month
- ----- 2026 -----
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- 7 participants
- 50354 messages
Re: [Pharo-users] Porting Transducers to Pharo
by phil@highoctane.be
Coupling this with Olek's work on the DataFrame could really come handy.
Phil
On Mon, Jun 5, 2017 at 9:14 AM, Stephane Ducasse <stepharo.self(a)gmail.com>
wrote:
> Hi Steffen
>
>
> > The short answer is that the compact notation turned out to work much
> better
> > for me in my code, especially, if multiple transducers are involved. But
> > that's my personal taste. You can choose which suits you better. In fact,
> >
> > 1000 take.
> >
> > just sits on top and simply calls
> >
> > Take number: 1000.
>
> To me this is much much better.
>
>
> > If the need arises, we could of course factor the compact notation out
> into
> > a separate package.
> Good idea
>
> Btw, would you prefer (Take n: 1000) over (Take number:
> > 1000)?
>
> I tend to prefer explicit selector :)
>
>
> > Damien, you're right, I experimented with additional styles. Right now,
> we
> > already have in the basic Transducer package:
> >
> > (collection transduce: #squared map * 1000 take. "which is equal to"
> > (collection transduce: #squared map) transduce: 1000 take.
> >
> > Basically, one can split #transduce:reduce:init: into single calls of
> > #transduce:, #reduce:, and #init:, depending on the needs.
> > I also have an (unfinished) extension, that allows to write:
> >
> > (collection transduce map: #squared) take: 1000.
>
> To me this is much mre readable.
> I cannot and do not want to use the other forms.
>
>
> > This feels familiar, but becomes a bit hard to read if more than two
> steps
> > are needed.
> >
> > collection transduce
> > map: #squared;
> > take: 1000.
>
> Why this is would hard to read. We do that all the time everywhere.
>
>
> > I think, this alternative would reads nicely. But as the message chain
> has
> > to modify the underlying object (an eduction), very snaky side effects
> may
> > occur. E.g., consider
> >
> > eduction := collection transduce.
> > squared := eduction map: #squared.
> > take := squared take: 1000.
> >
> > Now, all three variables hold onto the same object, which first squares
> all
> > elements and than takes the first 1000.
>
> This is because the programmer did not understand what he did. No?
>
>
>
> Stef
>
> PS: I played with infinite stream and iteration back in 1993 in CLOS.
> Now I do not like to mix things because it breaks my flow of thinking.
>
>
> >
> > Best,
> > Steffen
> >
> >
> >
> >
> >
> > Am .06.2017, 21:28 Uhr, schrieb Damien Pollet
> > <damien.pollet+pharo(a)gmail.com>:
> >
> >> If I recall correctly, there is an alternate protocol that looks more
> like
> >> xtreams or the traditional select/collect iterations.
> >>
> >> On 2 June 2017 at 21:12, Stephane Ducasse <stepharo.self(a)gmail.com>
> wrote:
> >>
> >>> I have a design question
> >>>
> >>> why the library is implemented in functional style vs messages?
> >>> I do not see why this is needed. To my eyes the compact notation
> >>> goes against readibility of code and it feels ad-hoc in Smalltalk.
> >>>
> >>>
> >>> I really prefer
> >>>
> >>> square := Map function: #squared.
> >>> take := Take number: 1000.
> >>>
> >>> Because I know that I can read it and understand it.
> >>> From that perspective I prefer Xtreams.
> >>>
> >>> Stef
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> On Wed, May 31, 2017 at 2:23 PM, Steffen Märcker <merkste(a)web.de>
> wrote:
> >>>
> >>>> Hi,
> >>>>
> >>>> I am the developer of the library 'Transducers' for VisualWorks. It
> was
> >>>> formerly known as 'Reducers', but this name was a poor choice. I'd
> like
> >>>> to
> >>>> port it to Pharo, if there is any interest on your side. I hope to
> learn
> >>>> more about Pharo in this process, since I am mainly a VW guy. And most
> >>>> likely, I will come up with a bunch of questions. :-)
> >>>>
> >>>> Meanwhile, I'll cross-post the introduction from VWnc below. I'd be
> very
> >>>> happy to hear your optinions, questions and I hope we can start a
> >>>> fruitful
> >>>> discussion - even if there is not Pharo port yet.
> >>>>
> >>>> Best, Steffen
> >>>>
> >>>>
> >>>>
> >>>> Transducers are building blocks that encapsulate how to process
> elements
> >>>> of a data sequence independently of the underlying input and output
> >>>> source.
> >>>>
> >>>>
> >>>>
> >>>> # Overview
> >>>>
> >>>> ## Encapsulate
> >>>> Implementations of enumeration methods, such as #collect:, have the
> >>>> logic
> >>>> how to process a single element in common.
> >>>> However, that logic is reimplemented each and every time. Transducers
> >>>> make
> >>>> it explicit and facilitate re-use and coherent behavior.
> >>>> For example:
> >>>> - #collect: requires mapping: (aBlock1 map)
> >>>> - #select: requires filtering: (aBlock2 filter)
> >>>>
> >>>>
> >>>> ## Compose
> >>>> In practice, algorithms often require multiple processing steps, e.g.,
> >>>> mapping only a filtered set of elements.
> >>>> Transducers are inherently composable, and hereby, allow to make the
> >>>> combination of steps explicit.
> >>>> Since transducers do not build intermediate collections, their
> >>>> composition
> >>>> is memory-efficient.
> >>>> For example:
> >>>> - (aBlock1 filter) * (aBlock2 map) "(1.) filter and (2.) map
> elements"
> >>>>
> >>>>
> >>>> ## Re-Use
> >>>> Transducers are decoupled from the input and output sources, and
> hence,
> >>>> they can be reused in different contexts.
> >>>> For example:
> >>>> - enumeration of collections
> >>>> - processing of streams
> >>>> - communicating via channels
> >>>>
> >>>>
> >>>>
> >>>> # Usage by Example
> >>>>
> >>>> We build a coin flipping experiment and count the occurrence of heads
> >>>> and
> >>>> tails.
> >>>>
> >>>> First, we associate random numbers with the sides of a coin.
> >>>>
> >>>> scale := [:x | (x * 2 + 1) floor] map.
> >>>> sides := #(heads tails) replace.
> >>>>
> >>>> Scale is a transducer that maps numbers x between 0 and 1 to 1 and 2.
> >>>> Sides is a transducer that replaces the numbers with heads an tails by
> >>>> lookup in an array.
> >>>> Next, we choose a number of samples.
> >>>>
> >>>> count := 1000 take.
> >>>>
> >>>> Count is a transducer that takes 1000 elements from a source.
> >>>> We keep track of the occurrences of heads an tails using a bag.
> >>>>
> >>>> collect := [:bag :c | bag add: c; yourself].
> >>>>
> >>>> Collect is binary block (reducing function) that collects events in a
> >>>> bag.
> >>>> We assemble the experiment by transforming the block using the
> >>>> transducers.
> >>>>
> >>>> experiment := (scale * sides * count) transform: collect.
> >>>>
> >>>> From left to right we see the steps involved: scale, sides, count
> and
> >>>> collect.
> >>>> Transforming assembles these steps into a binary block (reducing
> >>>> function)
> >>>> we can use to run the experiment.
> >>>>
> >>>> samples := Random new
> >>>> reduce: experiment
> >>>> init: Bag new.
> >>>>
> >>>> Here, we use #reduce:init:, which is mostly similar to #inject:into:.
> >>>> To execute a transformation and a reduction together, we can use
> >>>> #transduce:reduce:init:.
> >>>>
> >>>> samples := Random new
> >>>> transduce: scale * sides * count
> >>>> reduce: collect
> >>>> init: Bag new.
> >>>>
> >>>> We can also express the experiment as data-flow using #<~.
> >>>> This enables us to build objects that can be re-used in other
> >>>> experiments.
> >>>>
> >>>> coin := sides <~ scale <~ Random new.
> >>>> flip := Bag <~ count.
> >>>>
> >>>> Coin is an eduction, i.e., it binds transducers to a source and
> >>>> understands #reduce:init: among others.
> >>>> Flip is a transformed reduction, i.e., it binds transducers to a
> >>>> reducing
> >>>> function and an initial value.
> >>>> By sending #<~, we draw further samples from flipping the coin.
> >>>>
> >>>> samples := flip <~ coin.
> >>>>
> >>>> This yields a new Bag with another 1000 samples.
> >>>>
> >>>>
> >>>>
> >>>> # Basic Concepts
> >>>>
> >>>> ## Reducing Functions
> >>>>
> >>>> A reducing function represents a single step in processing a data
> >>>> sequence.
> >>>> It takes an accumulated result and a value, and returns a new
> >>>> accumulated
> >>>> result.
> >>>> For example:
> >>>>
> >>>> collect := [:col :e | col add: e; yourself].
> >>>> sum := #+.
> >>>>
> >>>> A reducing function can also be ternary, i.e., it takes an accumulated
> >>>> result, a key and a value.
> >>>> For example:
> >>>>
> >>>> collect := [:dic :k :v | dict at: k put: v; yourself].
> >>>>
> >>>> Reducing functions may be equipped with an optional completing action.
> >>>> After finishing processing, it is invoked exactly once, e.g., to free
> >>>> resources.
> >>>>
> >>>> stream := [:str :e | str nextPut: each; yourself] completing:
> >>>> #close.
> >>>> absSum := #+ completing: #abs
> >>>>
> >>>> A reducing function can end processing early by signaling Reduced
> with a
> >>>> result.
> >>>> This mechanism also enables the treatment of infinite sources.
> >>>>
> >>>> nonNil := [:res :e | e ifNil: [Reduced signalWith: res] ifFalse:
> >>>> [res]].
> >>>>
> >>>> The primary approach to process a data sequence is the reducing
> protocol
> >>>> with the messages #reduce:init: and #transduce:reduce:init: if
> >>>> transducers
> >>>> are involved.
> >>>> The behavior is similar to #inject:into: but in addition it takes care
> >>>> of:
> >>>> - handling binary and ternary reducing functions,
> >>>> - invoking the completing action after finishing, and
> >>>> - stopping the reduction if Reduced is signaled.
> >>>> The message #transduce:reduce:init: just combines the transformation
> and
> >>>> the reducing step.
> >>>>
> >>>> However, as reducing functions are step-wise in nature, an application
> >>>> may
> >>>> choose other means to process its data.
> >>>>
> >>>>
> >>>> ## Reducibles
> >>>>
> >>>> A data source is called reducible if it implements the reducing
> >>>> protocol.
> >>>> Default implementations are provided for collections and streams.
> >>>> Additionally, blocks without an argument are reducible, too.
> >>>> This allows to adapt to custom data sources without additional effort.
> >>>> For example:
> >>>>
> >>>> "XStreams adaptor"
> >>>> xstream := filename reading.
> >>>> reducible := [[xstream get] on: Incomplete do: [Reduced signal]].
> >>>>
> >>>> "natural numbers"
> >>>> n := 0.
> >>>> reducible := [n := n+1].
> >>>>
> >>>>
> >>>> ## Transducers
> >>>>
> >>>> A transducer is an object that transforms a reducing function into
> >>>> another.
> >>>> Transducers encapsulate common steps in processing data sequences,
> such
> >>>> as
> >>>> map, filter, concatenate, and flatten.
> >>>> A transducer transforms a reducing function into another via
> #transform:
> >>>> in order to add those steps.
> >>>> They can be composed using #* which yields a new transducer that does
> >>>> both
> >>>> transformations.
> >>>> Most transducers require an argument, typically blocks, symbols or
> >>>> numbers:
> >>>>
> >>>> square := Map function: #squared.
> >>>> take := Take number: 1000.
> >>>>
> >>>> To facilitate compact notation, the argument types implement
> >>>> corresponding
> >>>> methods:
> >>>>
> >>>> squareAndTake := #squared map * 1000 take.
> >>>>
> >>>> Transducers requiring no argument are singletons and can be accessed
> by
> >>>> their class name.
> >>>>
> >>>> flattenAndDedupe := Flatten * Dedupe.
> >>>>
> >>>>
> >>>>
> >>>> # Advanced Concepts
> >>>>
> >>>> ## Data flows
> >>>>
> >>>> Processing a sequence of data can often be regarded as a data flow.
> >>>> The operator #<~ allows define a flow from a data source through
> >>>> processing steps to a drain.
> >>>> For example:
> >>>>
> >>>> squares := Set <~ 1000 take <~ #squared map <~ (1 to: 1000).
> >>>> fileOut writeStream <~ #isSeparator filter <~ fileIn readStream.
> >>>>
> >>>> In both examples #<~ is only used to set up the data flow using
> reducing
> >>>> functions and transducers.
> >>>> In contrast to streams, transducers are completely independent from
> >>>> input
> >>>> and output sources.
> >>>> Hence, we have a clear separation of reading data, writing data and
> >>>> processing elements.
> >>>> - Sources know how to iterate over data with a reducing function,
> e.g.,
> >>>> via #reduce:init:.
> >>>> - Drains know how to collect data using a reducing function.
> >>>> - Transducers know how to process single elements.
> >>>>
> >>>>
> >>>> ## Reductions
> >>>>
> >>>> A reduction binds an initial value or a block yielding an initial
> value
> >>>> to
> >>>> a reducing function.
> >>>> The idea is to define a ready-to-use process that can be applied in
> >>>> different contexts.
> >>>> Reducibles handle reductions via #reduce: and #transduce:reduce:
> >>>> For example:
> >>>>
> >>>> sum := #+ init: 0.
> >>>> sum1 := #(1 1 1) reduce: sum.
> >>>> sum2 := (1 to: 1000) transduce: #odd filter reduce: sum.
> >>>>
> >>>> asSet := [:set :e | set add: e; yourself] initializer: [Set new].
> >>>> set1 := #(1 1 1) reduce: asSet.
> >>>> set2 := #(1 to: 1000) transduce: #odd filter reduce: asSet.
> >>>>
> >>>> By combining a transducer with a reduction, a process can be further
> >>>> modified.
> >>>>
> >>>> sumOdds := sum <~ #odd filter
> >>>> setOdds := asSet <~ #odd filter
> >>>>
> >>>>
> >>>> ## Eductions
> >>>>
> >>>> An eduction combines a reducible data sources with a transducer.
> >>>> The idea is to define a transformed (virtual) data source that needs
> not
> >>>> to be stored in memory.
> >>>>
> >>>> odds1 := #odd filter <~ #(1 2 3) readStream.
> >>>> odds2 := #odd filter <~ (1 to 1000).
> >>>>
> >>>> Depending on the underlying source, eductions can be processed once
> >>>> (streams, e.g., odds1) or multiple times (collections, e.g., odds2).
> >>>> Since no intermediate data is stored, transducers actions are lazy,
> >>>> i.e.,
> >>>> they are invoked each time the eduction is processed.
> >>>>
> >>>>
> >>>>
> >>>> # Origins
> >>>>
> >>>> Transducers is based on the same-named Clojure library and its ideas.
> >>>> Please see:
> >>>> http://clojure.org/transducers
> >>>>
> >>>>
> >
>
>
>
June 5, 2017
Re: [Pharo-users] Saving selected changes in Monticello
by Peter Uhnak
Komitter could indeed help you, see https://www.peteruhnak.com/blog/2016/08/12/fine-grained-committing-and-exte…
Peter
On Mon, Jun 05, 2017 at 08:27:23AM +0200, serge.stinckwich(a)gmail.com wrote:
> Kommiter available in default image allows you do cherry pick quite easily.
>
> Envoyé de mon iPhone
>
> > Le 5 juin 2017 à 07:14, Ben Coman <btc(a)openinworld.com> a écrit :
> >
> >
> >
> >> On Mon, Jun 5, 2017 at 10:12 AM, Andreas Sunardi <a.sunardi(a)gmail.com> wrote:
> >> I have a half done changes in my image, but I need to distribute the other changes that are done. I thought I was going to do this all at once, but now I realize I should split this into 2 commit versions.
> >>
> >> Is there a way in Monticello to say save my changes, but not this and that changes? I can't seem to find it nor in books. How do people deal with this situation?
> >
> > AFAIK, Monticello cannot cherry pick. One work around is probably something like...
> > 1. Save the mcz locally
> > 2. Merge back into a fresh image selecting only the bits you want.
> > 3. Save as a second mcz.
> > One issue is that the second mcz will have the first mcz as an ancestor, so before 2 you might create a new changeset to file out after 2, and load that into a second new image. yuck!
> >
> > Another alternative might be to use Tools > ChangeSorter to move the code you want to exclude from the mcz to a changeset, file that out and then revert that code in the image. After save the package to a mcz, reload the changeset.
> >
> > cheers -ben
June 5, 2017
Re: [Pharo-users] snap morphs/windows to each other when dragging
by Stephane Ducasse
I do not know that by default.
Especially since the drag and drop is when the mous is inside.
this is anice scenario for bloc :)
On Sun, Jun 4, 2017 at 2:51 PM, Peter Uhnak <i.uhnak(a)gmail.com> wrote:
> Hi,
>
> is it possible to tell windows to "snap" to other windows when dragging?
>
> So e.g. when I am dragging Playground window, it will try to snap side by side with a close-by another window, so they don't overlap (unless I e.g. press shift or something).
>
> Thanks,
> Peter
>
June 5, 2017
Re: [Pharo-users] Porting Transducers to Pharo
by Stephane Ducasse
Hi Steffen
> The short answer is that the compact notation turned out to work much better
> for me in my code, especially, if multiple transducers are involved. But
> that's my personal taste. You can choose which suits you better. In fact,
>
> 1000 take.
>
> just sits on top and simply calls
>
> Take number: 1000.
To me this is much much better.
> If the need arises, we could of course factor the compact notation out into
> a separate package.
Good idea
Btw, would you prefer (Take n: 1000) over (Take number:
> 1000)?
I tend to prefer explicit selector :)
> Damien, you're right, I experimented with additional styles. Right now, we
> already have in the basic Transducer package:
>
> (collection transduce: #squared map * 1000 take. "which is equal to"
> (collection transduce: #squared map) transduce: 1000 take.
>
> Basically, one can split #transduce:reduce:init: into single calls of
> #transduce:, #reduce:, and #init:, depending on the needs.
> I also have an (unfinished) extension, that allows to write:
>
> (collection transduce map: #squared) take: 1000.
To me this is much mre readable.
I cannot and do not want to use the other forms.
> This feels familiar, but becomes a bit hard to read if more than two steps
> are needed.
>
> collection transduce
> map: #squared;
> take: 1000.
Why this is would hard to read. We do that all the time everywhere.
> I think, this alternative would reads nicely. But as the message chain has
> to modify the underlying object (an eduction), very snaky side effects may
> occur. E.g., consider
>
> eduction := collection transduce.
> squared := eduction map: #squared.
> take := squared take: 1000.
>
> Now, all three variables hold onto the same object, which first squares all
> elements and than takes the first 1000.
This is because the programmer did not understand what he did. No?
Stef
PS: I played with infinite stream and iteration back in 1993 in CLOS.
Now I do not like to mix things because it breaks my flow of thinking.
>
> Best,
> Steffen
>
>
>
>
>
> Am .06.2017, 21:28 Uhr, schrieb Damien Pollet
> <damien.pollet+pharo(a)gmail.com>:
>
>> If I recall correctly, there is an alternate protocol that looks more like
>> xtreams or the traditional select/collect iterations.
>>
>> On 2 June 2017 at 21:12, Stephane Ducasse <stepharo.self(a)gmail.com> wrote:
>>
>>> I have a design question
>>>
>>> why the library is implemented in functional style vs messages?
>>> I do not see why this is needed. To my eyes the compact notation
>>> goes against readibility of code and it feels ad-hoc in Smalltalk.
>>>
>>>
>>> I really prefer
>>>
>>> square := Map function: #squared.
>>> take := Take number: 1000.
>>>
>>> Because I know that I can read it and understand it.
>>> From that perspective I prefer Xtreams.
>>>
>>> Stef
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> On Wed, May 31, 2017 at 2:23 PM, Steffen Märcker <merkste(a)web.de> wrote:
>>>
>>>> Hi,
>>>>
>>>> I am the developer of the library 'Transducers' for VisualWorks. It was
>>>> formerly known as 'Reducers', but this name was a poor choice. I'd like
>>>> to
>>>> port it to Pharo, if there is any interest on your side. I hope to learn
>>>> more about Pharo in this process, since I am mainly a VW guy. And most
>>>> likely, I will come up with a bunch of questions. :-)
>>>>
>>>> Meanwhile, I'll cross-post the introduction from VWnc below. I'd be very
>>>> happy to hear your optinions, questions and I hope we can start a
>>>> fruitful
>>>> discussion - even if there is not Pharo port yet.
>>>>
>>>> Best, Steffen
>>>>
>>>>
>>>>
>>>> Transducers are building blocks that encapsulate how to process elements
>>>> of a data sequence independently of the underlying input and output
>>>> source.
>>>>
>>>>
>>>>
>>>> # Overview
>>>>
>>>> ## Encapsulate
>>>> Implementations of enumeration methods, such as #collect:, have the
>>>> logic
>>>> how to process a single element in common.
>>>> However, that logic is reimplemented each and every time. Transducers
>>>> make
>>>> it explicit and facilitate re-use and coherent behavior.
>>>> For example:
>>>> - #collect: requires mapping: (aBlock1 map)
>>>> - #select: requires filtering: (aBlock2 filter)
>>>>
>>>>
>>>> ## Compose
>>>> In practice, algorithms often require multiple processing steps, e.g.,
>>>> mapping only a filtered set of elements.
>>>> Transducers are inherently composable, and hereby, allow to make the
>>>> combination of steps explicit.
>>>> Since transducers do not build intermediate collections, their
>>>> composition
>>>> is memory-efficient.
>>>> For example:
>>>> - (aBlock1 filter) * (aBlock2 map) "(1.) filter and (2.) map elements"
>>>>
>>>>
>>>> ## Re-Use
>>>> Transducers are decoupled from the input and output sources, and hence,
>>>> they can be reused in different contexts.
>>>> For example:
>>>> - enumeration of collections
>>>> - processing of streams
>>>> - communicating via channels
>>>>
>>>>
>>>>
>>>> # Usage by Example
>>>>
>>>> We build a coin flipping experiment and count the occurrence of heads
>>>> and
>>>> tails.
>>>>
>>>> First, we associate random numbers with the sides of a coin.
>>>>
>>>> scale := [:x | (x * 2 + 1) floor] map.
>>>> sides := #(heads tails) replace.
>>>>
>>>> Scale is a transducer that maps numbers x between 0 and 1 to 1 and 2.
>>>> Sides is a transducer that replaces the numbers with heads an tails by
>>>> lookup in an array.
>>>> Next, we choose a number of samples.
>>>>
>>>> count := 1000 take.
>>>>
>>>> Count is a transducer that takes 1000 elements from a source.
>>>> We keep track of the occurrences of heads an tails using a bag.
>>>>
>>>> collect := [:bag :c | bag add: c; yourself].
>>>>
>>>> Collect is binary block (reducing function) that collects events in a
>>>> bag.
>>>> We assemble the experiment by transforming the block using the
>>>> transducers.
>>>>
>>>> experiment := (scale * sides * count) transform: collect.
>>>>
>>>> From left to right we see the steps involved: scale, sides, count and
>>>> collect.
>>>> Transforming assembles these steps into a binary block (reducing
>>>> function)
>>>> we can use to run the experiment.
>>>>
>>>> samples := Random new
>>>> reduce: experiment
>>>> init: Bag new.
>>>>
>>>> Here, we use #reduce:init:, which is mostly similar to #inject:into:.
>>>> To execute a transformation and a reduction together, we can use
>>>> #transduce:reduce:init:.
>>>>
>>>> samples := Random new
>>>> transduce: scale * sides * count
>>>> reduce: collect
>>>> init: Bag new.
>>>>
>>>> We can also express the experiment as data-flow using #<~.
>>>> This enables us to build objects that can be re-used in other
>>>> experiments.
>>>>
>>>> coin := sides <~ scale <~ Random new.
>>>> flip := Bag <~ count.
>>>>
>>>> Coin is an eduction, i.e., it binds transducers to a source and
>>>> understands #reduce:init: among others.
>>>> Flip is a transformed reduction, i.e., it binds transducers to a
>>>> reducing
>>>> function and an initial value.
>>>> By sending #<~, we draw further samples from flipping the coin.
>>>>
>>>> samples := flip <~ coin.
>>>>
>>>> This yields a new Bag with another 1000 samples.
>>>>
>>>>
>>>>
>>>> # Basic Concepts
>>>>
>>>> ## Reducing Functions
>>>>
>>>> A reducing function represents a single step in processing a data
>>>> sequence.
>>>> It takes an accumulated result and a value, and returns a new
>>>> accumulated
>>>> result.
>>>> For example:
>>>>
>>>> collect := [:col :e | col add: e; yourself].
>>>> sum := #+.
>>>>
>>>> A reducing function can also be ternary, i.e., it takes an accumulated
>>>> result, a key and a value.
>>>> For example:
>>>>
>>>> collect := [:dic :k :v | dict at: k put: v; yourself].
>>>>
>>>> Reducing functions may be equipped with an optional completing action.
>>>> After finishing processing, it is invoked exactly once, e.g., to free
>>>> resources.
>>>>
>>>> stream := [:str :e | str nextPut: each; yourself] completing:
>>>> #close.
>>>> absSum := #+ completing: #abs
>>>>
>>>> A reducing function can end processing early by signaling Reduced with a
>>>> result.
>>>> This mechanism also enables the treatment of infinite sources.
>>>>
>>>> nonNil := [:res :e | e ifNil: [Reduced signalWith: res] ifFalse:
>>>> [res]].
>>>>
>>>> The primary approach to process a data sequence is the reducing protocol
>>>> with the messages #reduce:init: and #transduce:reduce:init: if
>>>> transducers
>>>> are involved.
>>>> The behavior is similar to #inject:into: but in addition it takes care
>>>> of:
>>>> - handling binary and ternary reducing functions,
>>>> - invoking the completing action after finishing, and
>>>> - stopping the reduction if Reduced is signaled.
>>>> The message #transduce:reduce:init: just combines the transformation and
>>>> the reducing step.
>>>>
>>>> However, as reducing functions are step-wise in nature, an application
>>>> may
>>>> choose other means to process its data.
>>>>
>>>>
>>>> ## Reducibles
>>>>
>>>> A data source is called reducible if it implements the reducing
>>>> protocol.
>>>> Default implementations are provided for collections and streams.
>>>> Additionally, blocks without an argument are reducible, too.
>>>> This allows to adapt to custom data sources without additional effort.
>>>> For example:
>>>>
>>>> "XStreams adaptor"
>>>> xstream := filename reading.
>>>> reducible := [[xstream get] on: Incomplete do: [Reduced signal]].
>>>>
>>>> "natural numbers"
>>>> n := 0.
>>>> reducible := [n := n+1].
>>>>
>>>>
>>>> ## Transducers
>>>>
>>>> A transducer is an object that transforms a reducing function into
>>>> another.
>>>> Transducers encapsulate common steps in processing data sequences, such
>>>> as
>>>> map, filter, concatenate, and flatten.
>>>> A transducer transforms a reducing function into another via #transform:
>>>> in order to add those steps.
>>>> They can be composed using #* which yields a new transducer that does
>>>> both
>>>> transformations.
>>>> Most transducers require an argument, typically blocks, symbols or
>>>> numbers:
>>>>
>>>> square := Map function: #squared.
>>>> take := Take number: 1000.
>>>>
>>>> To facilitate compact notation, the argument types implement
>>>> corresponding
>>>> methods:
>>>>
>>>> squareAndTake := #squared map * 1000 take.
>>>>
>>>> Transducers requiring no argument are singletons and can be accessed by
>>>> their class name.
>>>>
>>>> flattenAndDedupe := Flatten * Dedupe.
>>>>
>>>>
>>>>
>>>> # Advanced Concepts
>>>>
>>>> ## Data flows
>>>>
>>>> Processing a sequence of data can often be regarded as a data flow.
>>>> The operator #<~ allows define a flow from a data source through
>>>> processing steps to a drain.
>>>> For example:
>>>>
>>>> squares := Set <~ 1000 take <~ #squared map <~ (1 to: 1000).
>>>> fileOut writeStream <~ #isSeparator filter <~ fileIn readStream.
>>>>
>>>> In both examples #<~ is only used to set up the data flow using reducing
>>>> functions and transducers.
>>>> In contrast to streams, transducers are completely independent from
>>>> input
>>>> and output sources.
>>>> Hence, we have a clear separation of reading data, writing data and
>>>> processing elements.
>>>> - Sources know how to iterate over data with a reducing function, e.g.,
>>>> via #reduce:init:.
>>>> - Drains know how to collect data using a reducing function.
>>>> - Transducers know how to process single elements.
>>>>
>>>>
>>>> ## Reductions
>>>>
>>>> A reduction binds an initial value or a block yielding an initial value
>>>> to
>>>> a reducing function.
>>>> The idea is to define a ready-to-use process that can be applied in
>>>> different contexts.
>>>> Reducibles handle reductions via #reduce: and #transduce:reduce:
>>>> For example:
>>>>
>>>> sum := #+ init: 0.
>>>> sum1 := #(1 1 1) reduce: sum.
>>>> sum2 := (1 to: 1000) transduce: #odd filter reduce: sum.
>>>>
>>>> asSet := [:set :e | set add: e; yourself] initializer: [Set new].
>>>> set1 := #(1 1 1) reduce: asSet.
>>>> set2 := #(1 to: 1000) transduce: #odd filter reduce: asSet.
>>>>
>>>> By combining a transducer with a reduction, a process can be further
>>>> modified.
>>>>
>>>> sumOdds := sum <~ #odd filter
>>>> setOdds := asSet <~ #odd filter
>>>>
>>>>
>>>> ## Eductions
>>>>
>>>> An eduction combines a reducible data sources with a transducer.
>>>> The idea is to define a transformed (virtual) data source that needs not
>>>> to be stored in memory.
>>>>
>>>> odds1 := #odd filter <~ #(1 2 3) readStream.
>>>> odds2 := #odd filter <~ (1 to 1000).
>>>>
>>>> Depending on the underlying source, eductions can be processed once
>>>> (streams, e.g., odds1) or multiple times (collections, e.g., odds2).
>>>> Since no intermediate data is stored, transducers actions are lazy,
>>>> i.e.,
>>>> they are invoked each time the eduction is processed.
>>>>
>>>>
>>>>
>>>> # Origins
>>>>
>>>> Transducers is based on the same-named Clojure library and its ideas.
>>>> Please see:
>>>> http://clojure.org/transducers
>>>>
>>>>
>
June 5, 2017
Re: [Pharo-users] Porting Transducers to Pharo
by Stephane Ducasse
About NG this is strange but may be we were working on an old VW version.
- I can help producing a nice document :)
>>
>
> Do you mean like the booklets published over the last weeks? This would be
> great.
>
yes now I do not want to help promoting a syntax that alienates me (and
others because other people reported the saem to me). :)
Do you have an idea, how to add a package comment to the simple file-out it
> used? I think, a simple message send should suffice.
>
produce a monticello package, select the package and comment.
>>>
June 5, 2017
Re: [Pharo-users] Porting Transducers to Pharo
by Stephane Ducasse
We do not work with fileouts :)
You should produce packages with also a configuration and published them on
smalltalkhub or git and
in the MetaRepository.
You can also add package comments
On Sat, Jun 3, 2017 at 10:29 PM, Steffen Märcker <merkste(a)web.de> wrote:
> Dear all,
>
> attached are updated file-outs. I fixed a couple of annoyances that
> slipped through yesterday evening. Most notable:
>
> 1) Random generator now works.
> 2) Early termination via Reduced exception does MNU anymore.
> 3) Printing a transducer holding a block does not MNU anymore.
>
> Please, give it a spin and tell me your impressions. (At least) The
> coin-flipping the example from the package comment works now:
>
> scale := [:x | (x * 2 + 1) floor] map.
> sides := #(heads tails) replace.
> count := 1000 take.
> collect := [:bag :c | bag add: c; yourself].
> experiment := (scale * sides * count) transform: collect.
> "experiment cannot be re-used"
> samples := Random new
> reduce: experiment
> init: Bag new.
> "transform and reduce in one step"
> samples := Random new
> transduce: scale * sides * count
> reduce: collect
> init: Bag new.
> "assemble coin (eduction) and flip (reduction) objects"
> coin := sides <~ scale <~ Random new.
> flip := Bag <~ count.
> "flip coin =)"
> samples := flip <~ coin.
>
> Cheers!
> Steffen
>
>
>
> Am .06.2017, 23:08 Uhr, schrieb Steffen Märcker <merkste(a)web.de>:
>
> Thanks, this appears to work. Attached you'll find the file-out from
>> VisualWorks and the file-out from Pharo (includes package comment).
>>
>> Cheers!
>> Steffen
>>
>>
>> Am .06.2017, 20:06 Uhr, schrieb Yanni Chiu <yanni.code(a)gmail.com>:
>>
>> To get the extension methods into the Transducers package, the following
>>> worked for me - edit the category to have the prefix '*Transducers-'
>>>
>>> 2710c2710
>>>
>>> < !Number methodsFor: 'transforming' stamp: ' 2/6/17 15:38'!
>>>
>>> ---
>>>
>>> !Number methodsFor: '*Transducers-transforming' stamp: ' 2/6/17 15:38'!
>>>>
>>>
>>>
>>> On Fri, Jun 2, 2017 at 11:05 AM, Steffen Märcker <merkste(a)web.de> wrote:
>>>
>>> Dear all,
>>>>
>>>> thanks for the many suggestions. I didn't had time to test all
>>>> import/export ways yet. But for now, I can report on two:
>>>>
>>>> 1) NGFileOuter
>>>> Unfortunately It raised several MNUs in my image. I'll investigate them
>>>> later.
>>>>
>>>> 2) FileOut30 (VW Contributed)
>>>> I was able to file out the code except for the package definition.
>>>> Replacing {category: ''} in the class definitions with {package:
>>>> 'Transducers'} fixed that. However, methods that extend existing classes
>>>> did not end up in the Transducers package. Is there a similar easy
>>>> change
>>>> to the file-out making that happen? Also I'd like to add the package
>>>> comment if that's possible.
>>>>
>>>> Most things appear to work as far as I can see. Two exceptions:
>>>> 1) Random is a subclass of Stream in VW and in Pharo it is not. Hence,
>>>> I'll have to copy some methods from Stream to Random.
>>>> 2) I used #beImmutable in VW but I couldn't yet figure out how to make
>>>> objects immutable in Pharo.
>>>>
>>>> However, until the tests are ported, I cannot guarantee. Porting the
>>>> test
>>>> suite will be another beast, since I rely on the excellent
>>>> mocking/stubbing
>>>> library DoubleAgents by Randy Coulman. I am not sure how I will handle
>>>> that. In general, I think it would be really worth the effort to be
>>>> ported
>>>> to Pharo, too. DoubleAgents is pretty powerful and produces easy to read
>>>> and understand mocking/stubbing code. Personally, I prefer it clearly,
>>>> e.g., over Mocketry (no offence intended!).
>>>>
>>>> Attached you'll find the file-out that I loaded into Pharo. The issues
>>>> above are not addressed yet. However, the following example works:
>>>>
>>>> | scale sides count collect experiment random samples coin flip |
>>>> scale := [:x | (x * 2 + 1) floor] map.
>>>> sides := #(heads tails) replace.
>>>> count := 1000 take.
>>>> collect := [:bag :c | bag add: c; yourself].
>>>> experiment := (scale * sides * count) transform: collect.
>>>> random := #(0.1 0.3 0.4 0.5 0.6 0.7 0.8 0.9).
>>>>
>>>> samples := random
>>>> reduce: experiment
>>>> init: Bag new.
>>>>
>>>> samples := random
>>>> transduce: scale * sides * count
>>>> reduce: collect
>>>> init: Bag new.
>>>>
>>>> coin := sides <~ scale <~ random.
>>>> flip := Bag <~ count.
>>>>
>>>> samples := flip <~ coin.
>>>>
>>>>
>>>> Best, Steffen
>>>>
>>>>
>>>>
>>>> Am .06.2017, 08:16 Uhr, schrieb Stephane Ducasse
>>>> <stepharo.self(a)gmail.com
>>>> >:
>>>>
>>>> There is a package for that NGFileOuter or something like that on cincom
>>>>
>>>>> store.
>>>>> We used it for mobydic code.
>>>>>
>>>>> On Wed, May 31, 2017 at 6:35 PM, Alexandre Bergel <
>>>>> alexandre.bergel(a)me.com>
>>>>> wrote:
>>>>>
>>>>> If I remember correctly, there is a parcel in VisualWorks to export a
>>>>> file
>>>>>
>>>>>> out (Squeak format).
>>>>>>
>>>>>> @Milton, can you give a hand to Steffen?
>>>>>>
>>>>>> Alexandre
>>>>>> --
>>>>>> _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:
>>>>>> Alexandre Bergel http://www.bergel.eu
>>>>>> ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
>>>>>>
>>>>>>
>>>>>>
>>>>>> On May 31, 2017, at 10:32 AM, Steffen Märcker <merkste(a)web.de> wrote:
>>>>>>
>>>>>> Thanks for the encouraging response! First question: Which is the
>>>>>> recommended (friction free) way to exchange code between VW and Pharo?
>>>>>>
>>>>>> Cheers!
>>>>>> Steffen
>>>>>>
>>>>>> Am .05.2017, 16:22 Uhr, schrieb Alexandre Bergel <
>>>>>> alexandre.bergel(a)me.com
>>>>>> >:
>>>>>>
>>>>>> I second Sven. This is very exciting!
>>>>>>
>>>>>> Let us know when you have something ready to be tested.
>>>>>>
>>>>>> Alexandre
>>>>>>
>>>>>>
June 5, 2017
Re: [Pharo-users] STON Question
by Stephane Ducasse
Sven I know what Ston is :)
Now Pillar dev used Ston as input and it works. Then with some hooks they
let the user convert the strings
into specific objects and this is ok.
Now I want to add export in Ston and for configurations I need to control
for the application and not as a ston object the value saved.
For example a file reference should not be just a file reference in Ston
but
a string relative the the baseDirectory of the configuration.
So I thought that I can build a hook and control the call to Ston (convert
the object before to the correct values) and export.
It should be working now doing this I found myself redoing the output of
Ston.
So I will do it. Then I thought that may be I could reuse this logic.
Stef
On Sun, Jun 4, 2017 at 11:49 PM, Sven Van Caekenberghe <sven(a)stfx.eu> wrote:
> Stef,
>
> STON is like FUEL, it can write any object to a stream and read it back.
> Although you can customise how this is done, only one way of doing so is
> supported (i.e. you cannot change the format for each application).
>
> People have used STON for various applications now, including for
> configuration stuff (because it is a textual format that is easy to read).
>
> I would suggest you just try to see what your config looks like once you
> have the objects in Pharo.
>
> STON toStringPretty: myConfig.
>
> Then we can see how to tune the representation.
>
> Sven
>
> > On 4 Jun 2017, at 22:58, Stephane Ducasse <stepharo.self(a)gmail.com>
> wrote:
> >
> > Hi sven
> >
> > I'm working on a new configuration frameworks similar to the one use
> pillar.conf.
> >
> > So basically I have
> >
> > ston := '{
> > "newLine":#unix,
> > "separateOutputFiles":true
> > }'.
> > and I want to recreate it.
> >
> > So I did
> >
> > Object >> toConfigurationString
> >
> > ^ STON toString: self
> >
> > SimpleConfiguration >> exportStream
> >
> > ^ String streamContents: [ :str |
> > str << '{' .
> > self propertiesKeysAndValuesDo: [ :key :value|
> > str << key toConfigurationString.
> > str << ':'.
> > str << value toConfigurationString.
> > str << ','.
> > str lf.
> > ].
> > str << '}'.
> > str contents
> > ]
> >
> > The idea is that for special configuration values people should be able
> to define
> > how to go from the object to configuration.
> > For example for some FileReference I want to have just the path up to
> the baseDirectory.
> >
> > Now I wonder if I could not better reuse ston.
> > I saw the stonOn:
> > but I did not look further because I was wondering how the writer where
> passed.
> >
> > Any feedback is welcome.
> >
> > Stef (now bed time).
> >
>
>
>
June 5, 2017
Re: [Pharo-users] Saving selected changes in Monticello
by serge.stinckwich@gmail.com
Kommiter available in default image allows you do cherry pick quite easily.
Envoyé de mon iPhone
> Le 5 juin 2017 à 07:14, Ben Coman <btc(a)openinworld.com> a écrit :
>
>
>
>> On Mon, Jun 5, 2017 at 10:12 AM, Andreas Sunardi <a.sunardi(a)gmail.com> wrote:
>> I have a half done changes in my image, but I need to distribute the other changes that are done. I thought I was going to do this all at once, but now I realize I should split this into 2 commit versions.
>>
>> Is there a way in Monticello to say save my changes, but not this and that changes? I can't seem to find it nor in books. How do people deal with this situation?
>
> AFAIK, Monticello cannot cherry pick. One work around is probably something like...
> 1. Save the mcz locally
> 2. Merge back into a fresh image selecting only the bits you want.
> 3. Save as a second mcz.
> One issue is that the second mcz will have the first mcz as an ancestor, so before 2 you might create a new changeset to file out after 2, and load that into a second new image. yuck!
>
> Another alternative might be to use Tools > ChangeSorter to move the code you want to exclude from the mcz to a changeset, file that out and then revert that code in the image. After save the package to a mcz, reload the changeset.
>
> cheers -ben
June 5, 2017
Re: [Pharo-users] Saving selected changes in Monticello
by Ben Coman
On Mon, Jun 5, 2017 at 10:12 AM, Andreas Sunardi <a.sunardi(a)gmail.com>
wrote:
> I have a half done changes in my image, but I need to distribute the other
> changes that are done. I thought I was going to do this all at once, but
> now I realize I should split this into 2 commit versions.
>
> Is there a way in Monticello to say save my changes, but not this and that
> changes? I can't seem to find it nor in books. How do people deal with this
> situation?
>
AFAIK, Monticello cannot cherry pick. One work around is probably
something like...
1. Save the mcz locally
2. Merge back into a fresh image selecting only the bits you want.
3. Save as a second mcz.
One issue is that the second mcz will have the first mcz as an ancestor, so
before 2 you might create a new changeset to file out after 2, and load
that into a second new image. yuck!
Another alternative might be to use Tools > ChangeSorter to move the code
you want to exclude from the mcz to a changeset, file that out and then
revert that code in the image. After save the package to a mcz, reload the
changeset.
cheers -ben
June 5, 2017
Re: [Pharo-users] Wiring objects, IoC and Service Locator
by Ben Coman
Sorry I'm not familiar with the pattern, so I don't have a direct answer,
and the following might not have a direct answer either, but in general
I'd strongly suggest
two books for adapting from static OO languages.
1. The Design Patterns Smalltalk Companion, Alpert, Brown & Woolf -
including some discussion on why certain patterns required for static-OO
are redundant in Smalltalk. My naive guess is the Factory, Strategy and/or
Template patterns may apply.
2. A Mentoring Course on Smalltalk, Valloud
On Mon, Jun 5, 2017 at 5:17 AM, Vitor Medina Cruz <vitormcruz(a)gmail.com>
wrote:
> Hello,
>
> I would like to know how people in Pharo ecosystem do to deal with object
> wiring, as described by Marting Fowler in https://martinfowler.com/
> articles/injection.html#FormsOfDependencyInjection:
>
> "A common issue to deal with is how to wire together different elements:
> how do you fit together this web controller architecture with that database
> interface backing when they were built by different teams with little
> knowledge of each other."
>
> He gives an example, I will leave it in java as it is simple enough to
> understand:
>
> "class MovieLister...
>
> public Movie[] moviesDirectedBy(String arg) {
> List allMovies = finder.findAll();
> for (Iterator it = allMovies.iterator(); it.hasNext();) {
> Movie movie = (Movie) it.next();
> if (!movie.getDirector().equals(arg)) it.remove();
> }
> return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
>
> }"
>
> The question is how to provide the finder object in a decoupled matter, a
> naive approach would be:
>
> " private MovieFinder finder;
>
> public MovieLister() {
> finder = new ColonDelimitedMovieFinder("movies1.txt");
>
> }"
>
I'm not really familiar with IoC or DI patterns, so just taking your
example at face value, in Pharo I'd do...
MovieLister>>moviesDirectedBy: director
allMovies := finder allMovies.
^ allMovies select: [ :movie | movie getDirector = director ].
"although typically #getDirector would be renamed #director"
MovieLister>>finder: movieFinder
finder := movieFinder.
to be used like this...
lister := MovieLister new finder: (ColonDelimitedMovieFinder on:
'movies1.txt').
movies := lister moviesDirectedBy: 'Tarantino'.
>
> Which couples the MovieLister to the specific ColonDelimitedMovieFinder
> class.
>
> Fowler explains how to decouple using an IoC framework or a Service
> Locator. In Java and .Net IoC is used most of the time. I Googled how this
> problem is approached in Smalltalk/Pharo, and I generally I found answers
> "that is easy to do in Smalltalk, so there is no need of a framework", what
> I miss is a description on *how* to do that:
>
Just to satisfy my curiosity, I did find... "MVC was invented on Smalltalk
and is arguably the original Inversion of Control framework. ... I think
that IOC or the Dependency Injection pattern solves a problem that doesn't
really exist in the Smalltalk environment. Smalltalk is an untyped dynamic
language and uses message passing to communicate. This makes for objects
that are loosely coupled by nature at the language level"
http://www.weizhishi.tech/questions/296612/smalltalk-and-ioc
And this... "In an explicitly-typed language like C++, C#, or Java, it is
necessary to define an explicit interface for the injected collaborator.
The real collaborator and the substitute both have to implement the
interface. In a dynamically-typed language like Ruby [& Smalltalk],
explicit interfaces are not necessary. We can just take advantage of
'duck-typing'."
http://randycoulman.com/blog/2013/06/04/affordances-dependency-injection/
And GIlad Bracha knows a bit about Java... "I think that if youâre stuck
with a mainstream language, [dependency Injection] may be a reasonable work
around. It requires a significant degree of preplanning, and makes your
application dependent on one more piece of machinery that has nothing to do
with the actual problem the application is trying to solve. On the positive
side, it helps guarantee employment for software engineers. That said, itâs
important to understand that DIFs are just a work around for a deficiency
in the underlying language. So why not get rid of constructors and have a
class declaration create a factory object instead? Well, Smalltalk did just
that a generation ago. Every time you define a class, you define the
factory object for its instances."
https://gbracha.blogspot.com.au/2007/06/constructors-considered-harmful.html
btw, following Randy's example the two steps #new & #finder: should
hidden/combined by renaming MovieLister>>finder:
to MovieLister>>initializeWithFinder:
and adding...
MovieLister class >> newWithFinder: finder
^ self basicNew initializeWithFinder: finder
to be used like this...
lister := MovieLister newWithFinder: (ColonDelimitedMovieFinder on:
'movies1.txt').
movies := lister moviesDirectedBy: 'Tarantino'.
btw, while you are at it, if you haven't done this before, try implementing
this using Smalltalk's version of TDD...
https://www.youtube.com/watch?v=ymITEeAOtEA
https://www.youtube.com/watch?v=XxqZVAOomfM
In a clean image, first implement the following method and run the test,
then fill code as needed just-in-time...
MovieListerTest>>testColonDelimitedFinder
lister := MovieLister newWithFinder: (ColonDelimitedMovieFinder on:
'movies1.txt').
movies := lister moviesDirectedBy: 'Tarantino'.
self assert: (movies includes: 'Pulp Fiction')
> https://stackoverflow.com/questions/243905/smalltalk-and-ioc
> https://stackoverflow.com/questions/2684326/is-there-a-
> dependency-injection-framework-for-smalltalk
> https://stackoverflow.com/questions/243905/smalltalk-and-ioc/347477#347477
>
I see similar comments to my references in these. I hope the bits I've
focussed on are not redundant .
> I know that in Smalltalk I can make MovieLister to receive, upon
> construction, a class representing MovieFinder and call it construction
> message. As long an object that responds to this message is provided, I can
> create as many derivations I want and the MovieLister will be decoupled
> from the MovieFinder.
>
Reading this, I think maybe all I've done with my example is echo this. So
what I'm not clear on is what you mean by "wire things by hand"
> That way, however, I still have to wire things by hand, and I am not sure
> if this is what I am supposed to do in order to solve the decouple problem.
>
I would say use Pharo's TDD approach of coding-from-within-the-debugger.
The beauty of it is not that you get a lot of test coverage. There other
significant benefits are:
A. Your API ends up designed "in the most natural way to be used" since
each time you code how you want to use your API before you define the
implementation.
B. You only need to code the minimum to get that API aspect working. So
you get a faster feedback loop on your design.
C.
You consistency API implementation, you might define a test like...
MovieListerTest>>testAllFinders
AbstractMovieFinder allSubclasses do: [ :finderclass |
lister := MovieLister newWithFinder: (finderclass on: 'movies1.txt').
movies := lister moviesDirectedBy: 'Tarantino'.
self assert: (movies includes: 'Pulp Fiction'). ]
and then each time you add an AbstractMovieFinder subclass, you
implementation is again just work through fixing the tests from within the
debugger.
> Can you explain me how this is done in Pharo? It's is usually wiring by
> hand? Is there a simple construction that deals with the wiring problem
> that I cannot foresee?
>
TDD helps you avoid worrying about problems that might not exist.
cheers -ben
>
> Thanks in advance,
> Vitor
>
>
>
>
June 5, 2017