Re: Multimethods & derive

2009-06-08 Thread Peter Salvi

On Jun 8, 3:51 pm, Konrad Hinsen  wrote:

> See also my patch that creates such a universal root type [...]

Nice! That's exactly what I was thinking about

Peter

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Re: Multimethods & derive

2009-06-07 Thread Konrad Hinsen

On 08.06.2009, at 04:26, Stuart Sierra wrote:

>> If it is, it would be very useful to have something that is the
>> ancestor of everything (like T in common lisp).
>
> This has been thought about, at least: http://clojure.org/todo
> I think the question is... what should the universal ancestor be?  For
> classes, it's java.lang.Object.  But "derive" allows you to create
> relationships outside the Java class hierarchy.  So for now, defining
> your own "root" ancestor is the way to proceed.

See also my patch that creates such a universal root type (the  
keyword :root):

http://groups.google.com/group/clojure/browse_thread/thread/ 
bc5193304c1ab2e3/77e5d53d8ccd6b10?lnk=gst&q=%3Aroot#77e5d53d8ccd6b10

Of course, it is not a good idea to write code relying on an  
unofficial patch, I don't even use it myself for production code. But  
the patch illustrates that this can be done, and my tests have not  
shown any undesirable side effects.

Konrad.


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Re: Multimethods & derive

2009-06-07 Thread Stuart Sierra

On Jun 7, 7:46 pm, Peter Salvi  wrote:
> (defmethod foo [:bar :anything] [a b] ...)
> (defmethod foo [:anything :baz] [a b] ...)
> (defmethod foo [:bar :baz] [a b] ...)
>
> This seems to do the trick... but is this really the way to do it?

This looks reasonable.

> If it is, it would be very useful to have something that is the
> ancestor of everything (like T in common lisp).

This has been thought about, at least: http://clojure.org/todo
I think the question is... what should the universal ancestor be?  For
classes, it's java.lang.Object.  But "derive" allows you to create
relationships outside the Java class hierarchy.  So for now, defining
your own "root" ancestor is the way to proceed.

-Stuart Sierra
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Multimethods & derive

2009-06-07 Thread Peter Salvi

Hi,

I would like to use multimethods by dispatching on keys of the
variables (maps)
in a way that I sometimes have constraints on only some of the
arguments.

In common lisp I would say

(defgeneric foo (a b))
(defmethod foo ((a bar) b) ...)
(defmethod foo (a (b baz)) ...)
(defmethod foo ((a bar) (b baz)) ...)

This would mean that I have also defined methods for calls when (for
example)
`a' is an instance of `bar' and `b' of anything but `baz'. How can I
achieve
something like this with multimethods?

I could define a relationship like

(derive :bar :anything)
(derive :baz :anything)

and then do

(defmulti foo (fn [a b] [(:somekey1 a) (:somekey2 b)]))

with

(defmethod foo [:bar :anything] [a b] ...)
(defmethod foo [:anything :baz] [a b] ...)
(defmethod foo [:bar :baz] [a b] ...)

This seems to do the trick... but is this really the way to do it?

If it is, it would be very useful to have something that is the
ancestor of
everything (like T in common lisp).

Thanks,

Peter

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Re: multimethods + derive question

2008-11-19 Thread Rich Hickey



On Nov 19, 2:35 pm, Stuart Halloway <[EMAIL PROTECTED]> wrote:
> Rich,
>
> Very helpful, as always. Alias + the ability to pull in symbols names
> via refer was exactly what I was looking for.
>
> One scenario still worries me:
>
> 1. I create a multimethod that dispatches around a tag whose value is
> an unresolved keyword (:Foo instead of ::Foo). Everything works fine.
>
> 2. If at some later point I want the dispatch to depend on (derive),
> it is breaking change for clients to switch to from :Foo  to ::Foo.
>
> I am tempted to conclude that you should never use unqualified
> keywords as type tags, because you are exposing an implementation
> detail. That is, the implementation promises not to rely on derive.
>

Agreed. Qualified keywords or symbols as :tag values makes sense,
independent of derive, as many people will be using :tag, you can't
assert that you alone own the tag :Foo.

Rich
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Re: multimethods + derive question

2008-11-19 Thread Stuart Halloway

Rich,

Very helpful, as always. Alias + the ability to pull in symbols names  
via refer was exactly what I was looking for.

One scenario still worries me:

1. I create a multimethod that dispatches around a tag whose value is  
an unresolved keyword (:Foo instead of ::Foo). Everything works fine.

2. If at some later point I want the dispatch to depend on (derive),  
it is breaking change for clients to switch to from :Foo  to ::Foo.

I am tempted to conclude that you should never use unqualified  
keywords as type tags, because you are exposing an implementation  
detail. That is, the implementation promises not to rely on derive.

Cheers,
Stuart


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Re: multimethods + derive question

2008-11-19 Thread Rich Hickey



On Nov 19, 7:45 am, Stuart Halloway <[EMAIL PROTECTED]> wrote:
> Hi all,
>
> I am working on the multimethod chapter this week. This has required a
> lot of exploration, as the multimethod feature set goes well beyond
> what most people are using yet. I have hit one rough spot: derive. I
> have working code (below), but I don't like the way I have to call it
> with fully qualified keywords, e.g.
>
> (service-charge {:tag :examples.multimethods.service-charge-3/
> checking :balance 100})
> -> 25
> (service-charge {:tag :examples.multimethods.service-charge-3/
> checking :balance 1})
> -> 0
>
> I feel that I have made a wrong turn somewhere. Here are my assumptions:
>
> 1. I (the implementer) have to write my dispatch functions with
> qualified names, if I want to use derive.

Derive works only with qualified names for an important reason - so it
can be extensible without clashes. But it's important to logically
segregate hierarchical things from non, as namespace control is
orthogonal.

>
> 2. John Doe (the caller) must use fully qualified names *everywhere*.
> Since he does not live in my namespace he cannot use the ::.
>

Qualified names, yes, fully, no. In particular, :: supports aliases,
as shown below, or ` can be used with symbols.

> It's the latter that bothers me. It seems so ugly that I would never
> use hierarchical names for anything, which makes me think I am missing
> something. To make matters worse:
>
> 3. Once I use :: once on any keyword in my implementation, it is a
> quick slope to using it other places too, just so I don't have to
> remember which ones I chose to qualify and which ones I didn't. In the
> code below, :premium and :basic become ::premium and ::basic just for
> consistency with ::checking and ::savings.

Keywords are going to be used in different contexts for different
reasons, saying they should all use :: for consistency is sort of
punting on making decisions about their use.

>
> ---
> (ns examples.multimethods.service-charge-3)
>
> (defmulti account-level :tag)
> (println ::checking)
> (defmethod account-level ::checking [acct]
>(if (>= (:balance acct) 5000) ::premium ::basic))
> (defmethod account-level ::savings [acct]
>(if (>= (:balance acct) 1000) ::premium ::basic))
>
> (derive ::savings ::account)
> (derive ::checking ::account)
>
> (defmulti service-charge (fn [acct] [(account-level acct) (:tag acct)]))
> (defmethod service-charge [::basic ::checking]   [_] 25)
> (defmethod service-charge [::basic ::savings][_] 10)
> (defmethod service-charge [::premium ::account] [_] 0)

Here are two ways to do it that I would consider idiomatic, or at
least as intended. In both implementations, account tags are
hierarchical, and account levels are considered an enumeration,
account 'types' are capitalized. Consumer code uses normal namespace
management to deal with the names.

In the keyword version, note how you can use ::alias/name and get a
resolved keyword. In the symbol version note the consistent use of
syntax-quote, and the ability to pull in names using use/refer.

Other than the default resolution (symbols are resolved and keywords
aren't), they pretty much have parity here. In particular, note
symbols are functions of maps like keywords, a feature not used below
but important for their use as keys in maps.

;; using keywords ;;

(ns examples.multimethods.service-charge-3)

(defmulti account-level :tag)

(defmethod account-level ::Checking [acct]
   (if (>= (:balance acct) 5000) :premium :basic))
(defmethod account-level ::Savings [acct]
   (if (>= (:balance acct) 1000) :premium :basic))

(derive ::Savings ::Account)
(derive ::Checking ::Account)

(defmulti service-charge (fn [acct] [(account-level acct) (:tag
acct)]))
(defmethod service-charge [:basic ::Checking]   [_] 25)
(defmethod service-charge [:basic ::Savings][_] 10)
(defmethod service-charge [:premium ::Account] [_] 0)

(in-ns 'user)
(alias 'sc 'examples.multimethods.service-charge-3)

(sc/service-charge {:tag ::sc/Checking :balance 100})
(sc/service-charge {:tag ::sc/Checking :balance 1})


;; using symbols ;;

(ns examples.multimethods.service-charge-4)

(defmulti account-level :tag)
(defmethod account-level `Checking [acct]
   (if (>= (:balance acct) 5000) :premium :basic))
(defmethod account-level `Savings [acct]
   (if (>= (:balance acct) 1000) :premium :basic))

(declare Account Savings Checking)

(derive `Savings `Account)
(derive `Checking `Account)

(defmulti service-charge (fn [acct] [(account-level acct) (:tag
acct)]))
(defmethod service-charge [:basic `Checking]   [_] 25)
(defmethod service-charge [:basic `Savings][_] 10)
(defmethod service-charge [:premium `Account] [_] 0)


(in-ns 'user)
(refer 'examples.multimethods.service-charge-4)

(service-charge {:tag `Checking :balance 100})
(service-charge {:tag `Checking :balance 1})


Re: multimethods + derive question

2008-11-19 Thread mb

Hello stuart,

On 19 Nov., 13:45, Stuart Halloway <[EMAIL PROTECTED]> wrote:
> I am working on the multimethod chapter this week. This has required a  
> lot of exploration, as the multimethod feature set goes well beyond  
> what most people are using yet. I have hit one rough spot: derive. I  
> have working code (below), but I don't like the way I have to call it  
> with fully qualified keywords, e.g.

I feel not very familiar with multimethods up to now. I don't know
whether I'm using them correctly or not. (So a good thing, that you
investigate this for your book. :))

So take my interpretation with a good deal of salt.

Multimethods define an interface. Suppose for example the harness
question for test-is.

(defmulti report-result )

Now the librarian provides a function make-fancy-harness. John Doe
just calls this function and gets something. In fact he shouldn't even
bother, what it looks like. The only thing he has to know is, that
report-result does the right thing, when it is fed with that
something.
If John Doe looks inside and starts using internal information, I hope
he ends up in rework hell.

Now suppose John wants to extend the library. Now that's a different
story. Now he has to use the fully qualified keyword. Exactly once.
In derive. Compare this to class Foo extends
fully.quallified.ClassName.
After this point, :: is sufficient. For Jim, user of John's library,
the
same argument as above applies.

So in an ideal world fully qualified keywords should never ever show
up in user code. Only the defining namespace should use them.

The problem between : and :: is something I don't quite understand.
Either it is local to your namespace. Then it doesn't matter. Or you
leak it outside. Then :: should be used to avoid clashes. It can be
even used as an indicator for a part of the API. ("Change this and
user code will break.")

Accessing a field directly was already a bad idea back when we all
used C. And it brings inflexibility. For example, a deriver(?) wants
to replace a field, with a dynamically computed value? With direct
access you are off, with an interface function it easily possible.

Sincerely
Meikel


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



multimethods + derive question

2008-11-19 Thread Stuart Halloway

Hi all,

I am working on the multimethod chapter this week. This has required a  
lot of exploration, as the multimethod feature set goes well beyond  
what most people are using yet. I have hit one rough spot: derive. I  
have working code (below), but I don't like the way I have to call it  
with fully qualified keywords, e.g.

(service-charge {:tag :examples.multimethods.service-charge-3/ 
checking :balance 100})
-> 25
(service-charge {:tag :examples.multimethods.service-charge-3/ 
checking :balance 1})
-> 0

I feel that I have made a wrong turn somewhere. Here are my assumptions:

1. I (the implementer) have to write my dispatch functions with  
qualified names, if I want to use derive.

2. John Doe (the caller) must use fully qualified names *everywhere*.  
Since he does not live in my namespace he cannot use the ::.

It's the latter that bothers me. It seems so ugly that I would never  
use hierarchical names for anything, which makes me think I am missing  
something. To make matters worse:

3. Once I use :: once on any keyword in my implementation, it is a  
quick slope to using it other places too, just so I don't have to  
remember which ones I chose to qualify and which ones I didn't. In the  
code below, :premium and :basic become ::premium and ::basic just for  
consistency with ::checking and ::savings.

Is anybody else working with derive? What are your experiences?

Thanks,
Stu

---
(ns examples.multimethods.service-charge-3)

(defmulti account-level :tag)
(println ::checking)
(defmethod account-level ::checking [acct]
   (if (>= (:balance acct) 5000) ::premium ::basic))
(defmethod account-level ::savings [acct]
   (if (>= (:balance acct) 1000) ::premium ::basic))

(derive ::savings ::account)
(derive ::checking ::account)

(defmulti service-charge (fn [acct] [(account-level acct) (:tag acct)]))
(defmethod service-charge [::basic ::checking]   [_] 25)
(defmethod service-charge [::basic ::savings][_] 10)
(defmethod service-charge [::premium ::account] [_] 0)






--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---