In Java, Person.listToJson is a static call; it can't be abstracted, it's forever stuck in the global namespace. In Scala, the same call is to a value named Person whose class exposes a listToJson() method, which could come from any scope.
The Cake Pattern is one way to separate concerns, and swap out classes, objects, methods and values at compile time:
--
I dont think there is a way.
This illustrates an aspect of Scala's object keyword that's important
to understand: it refers to a particular fixed instance, not some type
that can be abstracted in any way. Thats a serious limitation and thus
objects should be referred to directly with care; there is no
capability to directly abstract over the reference (there are indirect
ways like Ken mentions).
Somewhat ironically, IMO this makes objects perhaps the least
"Object-oriented" feature in Scala. To me, OO-polymorphism is the
defining feature and the defining value of OO- methods in general, yet
Scala objects don't support polymorphic variation of their own
methods!
That leads us to ask "are Objects any better than static methods?" I
think so, in that they are true objects, albeit a fixed instance. That
enables them to /implement/ abstractions, if not /be/ abstractions
themselves:
object Person extends IPerson
val realPerson: IPerson
val mockPerson: IPerson = mock(IPerson)
> - Korny (missing rspec already)
Scala's classes are immutable, while Ruby's are mutable. Immutability
of classes is the only sane response if one uses a static type system
like Scala, or else all the types are suspect. OO- polymorphism
provides a disciplined way to publish and use runtime extension
points, while remaining consistent with a statically typed logic.
The choice of whether to use a statically typed language is a
fundamental one, that will deeply affect which designs, idioms and
processes prove effective in the project.
-Ben
--
You received this message because you are subscribed to the Google Groups "Melbourne Scala User Group" group.
To post to this group, send an email to scala...@googlegroups.com.
To unsubscribe from this group, send email to scala-melb+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/scala-melb?hl=en-GB.
Some thoughts:
Stable identifiers (ie vals, objects, function parameters) are a key concept in Scala, and it would break the type system if you could swap them at runtime. I can't really think how mocking objects would work. Having said that, you can redefine vals, objects and classes in the REPL, so maybe you can do something like this running as a script.
Your predicament doesn't feel like a real problem to me. It seems to me that your MainFilter class fundamentally has a dependency on "how to serialise a list of Persons to JSON", and in a project of any size, you would want this responsibility obtained/injected from elsewhere, as a function like Jeremy's example, or in some Jsonifier[List[Person]] class or some such. Even if you consider testing to be a different world, this would seem to be a pretty straightforward separation of concerns for the code itself.
On the other hand, you've said this is a quick throwaway prototype, and you don't want to overengineer it; fair enough, but what do unit tests achieve on a throwaway prototype?
Also, can't you do something like this with Mockito? (Just guessing, because I haven't used it)
class MainFilter {--
Wow. So all that rspec I wrote, that wasn't TDD either?
- K
On 03/02/2011 9:25 PM, "Jeremy Mawson" <jem.m...@gmail.com> wrote:
Hi Korny
The fact you need something like PowerMock indicates that you're not doing TDD. Change that and you won't have a problem. I understand it could and should be easier to write characterisation tests against legacy code, but it isn't yet.
And using functions is simple. Without functions, what you're doing is "Java without semicolons". Embrace the ƒ!
CheersJem
On 3 February 2011 19:04, Korny Sietsma <ko...@sietsma.com> wrote:
>
> Just a couple of notes on this:
>
> We're using the lift-json libraries, and yes you can just se...
--> You received this message because you are subscribed to the Google Groups "Melbourne Scala User Gr...
--
You received this message because you are subscribed to the Google Groups "Melbourne Scala User ...
You received this message because you are subscribed to the Google Groups "Melbourne Scala User Group" group.
Despite the fact that what I said above is true (ie direct/bare refs
to Scala Objects are not abstractable, leading to downstream
consequences like difficulty with mocking), I think we all must agree
that its very damn /convenient/ to just refer to an object relative to
a global namespace.
Is there some way we can have our cake and eat it too?
For example, imagine we're using a library that exposes a useful
family of Foo objects, and we want to make a "normal" regular Foo in
our client code.
package com.acme
trait Foo
class StandardFoo extends Foo
object Foo {
def standardFoo: Foo = new StandardFoo
}
Its probably alot less fuss to write "com.acme.Foo.standardFoo" than
using a FooFactory obtained the Cake pattern (I still dont understand
it properly!) or injection solutions like Spring/Guice. But can we do
this, and still retain the flexibility to vary the impl of standardFoo
at runtime, such a returning a MockFoo?
My own response to this challenge has been to make methods of scala
objects "ultra-thin" - they immediately redirect to an implicit
parameter which is the true "foo factory":
package com.acme
class Foo
object Foo {
def standardFoo(implicit factory: FooFactory) = factory.newFoo
implicit object defaultFooFactory extends FooFactory {
def newFoo = new StandardFoo
}
}
trait FooFactory {
def newFoo: Foo
}
This allows us 3 nice things which are quite hard to all have at the same time:
1. We can create standardFoos very easily with "com.acme.Foo.standardFoo"
2. By default we will get a StandardFoo back without having to do anything
3. If we want to override the default FooFactory, we just need to have
an alternate FooFactory in implicit scope and the library will use it
preferentially.
Scala really can be a joy to discover :)
-Ben
-Ben
-Ben
--
You received this message because you are subscribed to the Google Groups "Melbourne Scala User Group" group.
To post to this group, send an email to scala...@googlegroups.com.
To unsubscribe from this group, send email to scala-melb+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/scala-melb?hl=en-GB.
This is quite nice! I wonder how it would look if you used it everywhere? I suspect it would work fine, but 100 implicit dependencies floating around scares me a bit; working out implicit precedence in your head can be tricky.
On 03/02/2011 11:30 PM, "Ben Hutchison" <brhut...@gmail.com> wrote:On Thu, Feb 3, 2011 at 8:59 AM, Ben Hutchison <brhut...@gmail.com> wrote:
> ... Scala's object keyword...refers to a particular fixed instance, not some type
> that can be abstracted in any way. Thats a serious limitation and thus
> objects should be referre...
-Ben
--
You received this message because you are subscribed to the Google Groups "Melbourne Scal...
Good question! Ive spent some time thinking and experimenting to try
to get an answer for that. Its fair to say there are some problems...
Consider for example the following code we might wish to
interaction-test (based on my earlier example code):
object SUT {
def happyFoo = Foo.standardFoo.beHappy()
}
(In the example unit test sketch below, I'll replace the implicit
param FooFactory with a function Unit=>Foo, (relative to my earlier
example) to make the code simpler)
val aHappyFoo = Foo.standardFoo
val mockFoo = mock(Foo)
//record expectation
expect(mockFoo.beHappy()).andReturn(aHappyFoo)
implicit val fooFactory = ()=> mockFoo
SUT.happyFoo.should_== aHappyFoo
This wont work without some extra changes :(. We need to get the
implicit fooFactory into scope in SUT, which requires its
co-operation:
object SUT {
def happyFoo(implicit fooFactory: ()=>Foo) = Foo.standardFoo.beHappy()
}
Ive tried this style out in a larger codebase, my fledgling
Vector/Matrices/Geometry library. There's a tendency to accumulate
implicit param "line-noise" in all the signatures. For example,
To make Polygon one must pass an implicit PolygonFactory "pf", but
also, "pf" needs to be passed an implicit LineFactory "lf", and "lf"
needs to be passed a VectorFactory, which needs to be passed a
Numeric[T]-like instance, and also a ClassManifest[T] if we wish to
instantiate any Array's of primitives. Sigh....
What Im currently investigating is whether all these implicit params,
which essentially define the "configuration" of the library, can be
rolled into a single Configuration implicit param that's threaded
around:
implicit object MyConfiguration {
implicit val PolygonFactory = ...
implicit val LineFactory = ...
implicit val VectorFactory = ...
}
Nothing definite yet...
As an aside, several weeks ago I was searching around for some prior
art to inform my design when I noticed paper circa 2005 by Oleg
Kiselyov, a much revered Haskell programmer, on a similar topic:
Functional Pearl: Implicit Configurations:
"The configurations problem is to propagate run-time preferences
throughout a program, allowing multiple concurrent configuration
sets to coexist safely under statically guaranteed separation. This
problem is common in all software systems, but particularly acute
in Haskell, where currently the most popular solution relies on unsafe
operations and compiler pragmas."
[http://www.cs.rutgers.edu/~ccshan/prepose/p1214-kiselyov.pdf]
I wont claim to understand it (yet). My takeaway is that the problem
is non-trivial and worthy of effort to find good solutions.
-Ben
To make Polygon one must pass an implicit PolygonFactory "pf", but
also, "pf" needs to be passed an implicit LineFactory "lf", and "lf"
needs to be passed a VectorFactory, which needs to be passed a
Numeric[T]-like instance, and also a ClassManifest[T] if we wish to
instantiate any Array's of primitives. Sigh....
Thanks for your detailed explanation. Its good to have someone like
yourself who really gets the Cake pattern locally to learn off.
Cake seems to have the compile-time configuration problem pretty
nicely addressed. For configuration at /runtime/ however, I still feel
that implicit param approaches are likely to prove successful. It
comes down to point (b) in your list below. Is that a show stopper for
some use cases?
Eg If I publish a compiled library/component to Maven, or load it into
an OSGI container, then it might be too late for the component's
clients to use compile time configuration.
Maybe not, if eg the component was compiled "abstract" (eg with yet
unmet self-types), and I then compile some extra code to tie it into a
configuration at container load time, just before execution. Michael
During at Day Software (large Swiss vendor of Java content repos ala
Jackrabbit) might be thinking along these lines, based on a talk he
gave at Scaladays.
-Ben
Cake seems to have the compile-time configuration problem pretty
nicely addressed. For configuration at /runtime/ however, I still feel
that implicit param approaches are likely to prove successful. It
comes down to point (b) in your list below. Is that a show stopper for
some use cases?