
Wizards and warriors, part five - tomaskafka
http://ericlippert.com/2015/05/11/wizards-and-warriors-part-five/
======
Retra
This seems to me to be a classic example of poorly implementing first-class
functions in an OO language to solve a simple problem. Or rather, this
solution bestows a level of needless complexity and jargon that would make a
LISPer cry.

As soon as you want to start encoding "rules" for "encoding the allowed
transformations of your data," then congratulations, you've invented something
called a "function." Functions are rules for expressing what you are allowed
to do.

    
    
        fn wield(Wizard, Wand) // Wizards can wield wands.
        fn wield(Warrior, Sword) // Warriors can wield swords.
        fn capability(Player, wield_fn=wield(Player, Weapon)) // Players can wield weapons.
    

Now there's no way to violate these rules unless your users can compile
functions and pass them into your code.

>There is no need to figure out “which class does the rule about paladins vs
werewolves go in?” The rule goes in a rulebook, end of story. Like I said,
Inform7 is amazing.

I've never seen someone so impressed by global functions before.

~~~
Guvante
What language would that work in? C# only does compile time dispatch of
overloaded methods and Common LISP doesn't have overloaded methods at all.

I honestly can't think of a language that even support overloading of methods
based on run time types.

And the moment you don't have overloaded methods based on run time types you
have to do something interesting like this to do the work.

~~~
ajuc
> I honestly can't think of a language that even support overloading of
> methods based on run time types.

Clojure does with multimethods (
[http://clojure.org/runtime_polymorphism](http://clojure.org/runtime_polymorphism)
). I suspect lisp with meta object protocol does too, but I only heard about
it, so can't confirm.

EDIT: deleted my solution that worked without method overloading at all, cause
I missed some constraints.

~~~
ajuc
Just an example:

    
    
        (derive ::wizard ::person)
        (derive ::warior ::person)
    
        (derive ::weapon ::thing)
        (derive ::dagger ::weapon)
        (derive ::sword ::weapon)
        (derive ::staff ::weapon)
    
        ;; defines dispatch function that returns (inheritable) categories to dispath
        ;; it's very simple in this case
        (defmulti wield! (fn [p1 p2 thing]
                            [(type p2) (type thing)]))
    
        (defmethod wield! [::person ::thing] [p1 p2 thing]
                  (say! p1 p2 " refuse"))
        (defmethod wield! [::person ::dagger] [p1 p2 thing]
                  (take! p2 p1 thing)
                  (say! p1 p2 "accepts."))
        (defmethod wield! [::wizard ::staff] [p1 p2 thing]
                  (take! p2 p1 thing)
                  (say! p1 p2 " gladly accepts."))
        (defmethod wield! [::warrior ::sword] [p1 p2 thing]
                  (take! p2 p1 thing)
                  (say! p1 p2 " gladly accepts."))
    

It obviously could be done in half the lines without using multimethods, but
the result would be much less extensible.

BTW, with just 2 lines you can add gandalf and it just works (because there
are no conflicting methods right now).

    
    
        (derive ::gandalf ::wizard)
        (derive ::gandalf ::warrior)
    

If there were any - you can set preferences to avoid errors.

------
dbpokorny
A wild UC Berkeley CS 61A adventure appears!

[https://inst.eecs.berkeley.edu/~cs61a/su13/lab/lab06a/lab06a...](https://inst.eecs.berkeley.edu/~cs61a/su13/lab/lab06a/lab06a.txt)

You can stick your pseudocode into the "give" function:
[https://inst.eecs.berkeley.edu/~cs61a/sp15/lab/lab10/adventu...](https://inst.eecs.berkeley.edu/~cs61a/sp15/lab/lab10/adventure.py)

I'm not sure it's _explicit_ in the original article, but I tend to agree with
the sentiment that it's a _lot_ more fun to learn about interpreters and
databases when you're making games rather than corporate infrastructure
(although I can't deny that building corporate infrastructure has its moments)

Joel Spolsky talks about using the compiler to catch errors on p. 68 of "The
Best Software Writing I"

[https://books.google.com/books?id=vPvhzDqZlaAC&pg=PA68&lpg=P...](https://books.google.com/books?id=vPvhzDqZlaAC&pg=PA68&lpg=PA68&dq=joel+spolsky+type+compiler&source=bl&ots=cm2hRdP9W5&sig=I3gcA35aUfqoKCcQoiIm-
CM6CvY&hl=en&sa=X&ved=0CFUQ6AEwCGoVChMI2YzG6KSUyQIVBcNjCh1oQAcK#v=onepage&q=joel%20spolsky%20type%20compiler&f=false)

------
inglor
I don't understand this post at all. He started with wanting compile time
validation of the invariant ("A wizard cannot wield a sword") - then talked
about how runtime solutions were not good and then ended up with a solution
that __does not verify that invariant in compile time at all __.

He just ended up with a language where all code is written in a specific
language programmers have to learn and errors are in another language.

The generic constraint solution he had in the middle (part 2) was actually
pretty decent because hey: if Wizards and Warriors can't yield the same
weapons - and the weapon is a property of the supertype then __they are not
interchangeable as players__. You need a different interface.

------
archimedespi
Nice approach; this is a common sort of dilemma for API design.

------
askafriend
For anyone else who got excited about basketball...this isn't about the NBA :(

