Pharo-users
By thread
pharo-users@lists.pharo.org
By month
Messages by month
- ----- 2026 -----
- 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
June 2017
- 100 participants
- 622 messages
Re: [Pharo-users] Wiring objects, IoC and Service Locator
by Vitor Medina Cruz
Thanks for the answer Ben and Stephane.
I already read A Mentoring Course on Smalltalk, Valloud, there is nothing
there I could use in this case :( . I will look after for The Design
Patterns Smalltalk Companion. Most of the sources provided I already know
of or went in the same lines lines of what I have already found.
About TDD, I am experienced with the discipline and have tested it on Pharo
living system already, but I could not understand how this is related with
object wiring, DI and service locator.
>From ben:
"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'."
and Stephane:
Why don't you simply pass the class and use that class in your MovieLister?
>
> MovieLister new
> finderClass: MySuperCoolFinderClass
>
> ...
> MovieLister finder
> finderClass new .....
>
> What is wrong with that.
That was what I meant when I said: "I know that in Smalltalk I can make
MovieLister to receive, upon construction, a class representing MovieFinder
and call it construction message.". The code I had in mind is a bit of mix
from the one provided by you both:
MovieLister>>moviesDirectedBy: director
allMovies := finder allMovies.
^ allMovies select: [ :movie | movie getDirector = director ].
"although typically #getDirector would be renamed #director"
MovieLister>>finder: aMovieFinderBuilder
finder := aMovieFinderClass new.
to be used like this...
lister := MovieLister new finder: (ColonDelimitedMovieFinder builderOn:
'movies1.txt').
movies := lister moviesDirectedBy: 'Tarantino'."
But that means I will have to wire dependencies by hand whenever I create a
MovieLister and seek through code when and if those dependencies change.
When there are lot's of dependencies it's is a considerable and tedious
work. Let's see an image from Fowlers article:
[image: Inline image 1]
In this case, the service locator provides me with an instance and I
configure the instance in the assembler, the scheme is alike for an IoC,
and that would mean my implementation could be like this:
MovieLister>>moviesDirectedBy: director
allMovies := finder allMovies.
^ allMovies select: [ :movie | movie getDirector = director ].
"although typically #getDirector would be renamed #director"
MovieLister>>initialize
finder := ServiceLocator locate: FinderClass <--- This would bring
the instance of finder class configured by the assembler
to be used like this...
lister := MovieLister new.
movies := lister moviesDirectedBy: 'Tarantino'."
and the assembler:
Assember class>>configure:
aMap put: (ColonDelimitedMovieFinder builderOn: 'movies1.txt') at:
FinderClass
My assembler and service locator could be even more elaborated, and provide
a different MovieFinder in test scope, for different classes or wharever.
It is a little convenience for Smalltalk, I will give that, but I was
wandering if there was something alike in Pharo, by your answers I assuming
there is nothing like that.
If you do not want to have a reference at runtime to a Finder then you
> need to use announcement and registration.
I didn't understand that, could you elaborate?
On Mon, Jun 5, 2017 at 6:41 AM, Stephane Ducasse <stepharo.self(a)gmail.com>
wrote:
> Why don't you simply pass the class and use that class in your MovieLister?
>
> MovieLister new
> finderClass: MySuperCoolFinderClass
>
> ...
> MovieLister finder
> finderClass new .....
>
> What is wrong with that.
>
> If you do not want to have a reference at runtime to a Finder then you
> need to use announcement and registration.
>
> Stef
>
>
>
> On Sun, Jun 4, 2017 at 11:17 PM, 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");
> >
> > }"
> >
> > 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:
> >
> > 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 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. 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.
> >
> > 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?
> >
> > Thanks in advance,
> > Vitor
> >
> >
> >
>
>
June 5, 2017
XPath has unresolved dependencies
by Henrik Nergaard
Hi,
Loading XPath from the catalog browser into the latest image raises a warning that there are unresolved references, and that the package depends on the following classes: XMLHighlighter, XMLHighlighterDefaults, GLMXMLHighlighterTextStylerDecorator.
The regular XPath works fine ignoring the warnings, but inspecting an XML document and clicking the XPath pane gives an error.
Best regards,
Henrik
June 5, 2017
Re: [Pharo-users] Deprecating Auto-fix: WOW
by Stephane Ducasse
:)
little improvements one at a time but many many many times :)
On Sat, Jun 3, 2017 at 3:32 PM, Sean P. DeNigris <sean(a)clipperadams.com> wrote:
> Either I died and went to heaven, or my Pharo 6 image just ported all my
> deprecated methods auto-magically. Very cool! Thanks!!!
>
>
>
> -----
> Cheers,
> Sean
> --
> View this message in context: http://forum.world.st/Deprecating-Auto-fix-WOW-tp4949164.html
> Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
>
June 5, 2017
Re: [Pharo-users] Wiring objects, IoC and Service Locator
by Stephane Ducasse
Why don't you simply pass the class and use that class in your MovieLister?
MovieLister new
finderClass: MySuperCoolFinderClass
...
MovieLister finder
finderClass new .....
What is wrong with that.
If you do not want to have a reference at runtime to a Finder then you
need to use announcement and registration.
Stef
On Sun, Jun 4, 2017 at 11:17 PM, 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");
>
> }"
>
> 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:
>
> https://stackoverflow.com/questions/243905/smalltalk-and-ioc
> https://stackoverflow.com/questions/2684326/is-there-a-dependency-injection…
> https://stackoverflow.com/questions/243905/smalltalk-and-ioc/347477#347477
>
> 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. 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.
>
> 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?
>
> Thanks in advance,
> Vitor
>
>
>
June 5, 2017
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