Arc Forumnew | comments | leaders | submitlogin
Customise meaning in function position for new types
1 point by chris_l 6009 days ago | 7 comments
I might have missed this, but is there a built-in way to define the meaning of (obj key) -> val for a new type? Or to define a new type in any sense? It is built in for tables and templates, but I can't quite figure out where.


4 points by absz 6009 days ago | link

That depends on whether you're using the default arc2.tar or if you're using Anarki (the git wiki with community changes---you should use it, it's better). The answer to the second question (define a new type) is yes either way:

  (annotate my-type my-obj)
creates a new object with type my-type and representation my-obj. You can get the type with (type obj) and the representation with (rep obj). Types, however, have no associated behaviour.

The answer to the first question is no in arc2.tar (it's handled by the interpreter directly), but yes on Anarki:

  (defcall type-name (self arg)
    (do-something self arg))
defcall is like def, except instead of a function name you provide a type name. Then, whenever an object of that type is called in function position, its representation is passed in as the first argument (in that example, self), and the other arguments are passed to the rest of the parameter list. For instance, take a "shouter" type which wraps an output port and, when called, prints to that port in all uppercase:

  arc> (defcall shouter (self str)
         (w/stdout self (prn:upcase str))
         str)
  #<procedure>
  arc> (= yell (annotate 'shouter (stderr)))
  #3(tagged shouter #<output-port:stderr>)
  arc> (yell "Hello, world!")
  HELLO, WORLD!
  "Hello, world!"
And that's how you create objects of new types and make them callable.

-----

2 points by rincewind 6007 days ago | link

Can you define a setter for my-type annotated objects? Like this:

  (= tab (annotate 'my-table (table)))
  (defcall my-table (self arg)
     self.arg)

  ;what i'd like to do:
  (defset 'my-table ((self arg) value)
      (= self.arg upcase.value))

  (= (tab 'test) "foo bar baz")

  (tab 'test)
  ;should return ("FOO BAR BAZ")
If not, it should not be too hard to implement, but I would rather not put redundant effort into it.

-----

1 point by almkglor 6007 days ago | link

An alternative is to use the "lib/settable-fn.arc" or "lib/settable-fn2.arc" frameworks for this.

When using settable-fn.arc:

  (def create-my-type ()
    (add-attachments
      'key (fn () (generate-keys))
               ; note the order: value before key(s)
      '=   (fn (v k)  (assign-value-v-to-key-k v k))
      (annotate 'my-type
        (fn (k)
          (lookup-in-k k)))))
lib/settable-fn2.arc:

  (def create-my-type ()
    (add-attachments
      'key (fn () (generate-keys))
      '=   (fn (v k)  (assign-value-v-to-key-k v k))
      'type 'my-type
      (fn (k)
        (lookup-in-k k))))
Aside from 'keys and '=, it allows you to overload 'len

-----

2 points by almkglor 6007 days ago | link

The "Create your own collection" series should help a bit - these collections all use lib/settable-fn.arc, and with minimal modification should be useable with lib/settable-fn2.arc

http://arclanguage.com/item?id=3595 Suggest PG: Settable function objects

http://arclanguage.com/item?id=3698 Create your own collection in Arc: settable functions now implemented on arc-wiki.git

http://arclanguage.com/item?id=3762 Create your own collection: use directories as if they were tables with file-table

http://arclanguage.com/item?id=3858 Create your own collection: bidirectional tables

http://arclanguage.com/item?id=5254 Create your own collection: cached-table

http://arclanguage.com/item?id=7365 Create your own collection: proto-table, when you want prototyping semantics in your object system

-----

1 point by absz 6007 days ago | link

Yes, you can; it involves redefining sref. When you write (= (obj key) value), it becomes (sref obj value key). So in this case, you would write

  (redef sref (x v k)
    (if (isa x 'my-table)
      (let y (rep x)
        (annotate 'my-table (= y.k (upcase v))))
      (old x v k)))
Or, if you're using nex3's defm,

  (defm sref ((t x my-table) v k)
    (let y (rep x)
      (annotate 'my-table (= y.k (upcase v)))))
If you do this a lot, you could probably wrap a macro around it to eliminate some of the boilerplate.

-----

1 point by stefano 6007 days ago | link

'= is a macro, and therefore it needs to know how to assign to the variable at compile time, but the type information is known only at run time, but it should be possible to get something like:

  (= (my-table tab 'test) "foo bar baz")
to work. It's a little more verbose and if you changed the name of the type from my-table to something else you would have to change every assignment.

-----

1 point by chris_l 6009 days ago | link

Thanks, that's just what I was looking for!

-----