How do I get a procedure from either a symbol or a string representing the procedure name?
If the procedures are in a namespace, then you use namespace-variable-value
.
If only a certain set of procedures are allowed/needed consider using a hash table instead.
This is for a job queue, so I’m storing the name of the function in the database along with a serialized list of argument values.
I should probably store both the module and function name to avoid having the job queue manager having to require everything.
Your tip got me to the “Reflection & Security” section, so I can dig in to that a bit :slightly_smiling_face:
In your use case I would probably use two hash tables: one from function to name and one the other way.
I suppose that might be ok, it just requires “registering” the job functions ahead of time.
How would this be done if I didn’t want to register the functions in a hash table? The following complained about foo
not being defined: #lang racket
;; (serialize obj) -> string?
;; obj : (or/c hash? list? vector?)
(define (serialize obj)
(let ([ostr (open-output-string)])
(write obj ostr)
(get-output-string ostr)))
;; (deserialize str) -> (or/c hash? list? vector?)
;; str : string?
(define (deserialize str)
(read (open-input-string str)))
(define (foo a b)
(format "a is ~a, b is ~a" a b))
(define serial2 (compose deserialize serialize))
(define fun (serial2 'foo))
(define args (serial2 '("hello" 78)))
(apply (namespace-variable-value fun) args)
i.e. if I have the module name and procedure name as symbols or strings
You need to get the namespace “inside” a module first.
I am looking for an example - can’t remember the exact incantation.
(define-namespace-anchor here)
(define ns (namespace-anchor->namespace here))
Then wrap namespace-variable-value
in a parameterization of current-namespace.
Hmm… that seems a bit convoluted :slightly_smiling_face: I’m used to Ruby, where if I had a class name and a function name, executing the function is trivial.
i.e. I would expect to be able to do something like (define fun (get-module-function 'Foo 'bar))
There is also dynamic-require
@badkins Fwiw you can use the fourth argument of namespace-variable-value instead of using current-namespace.
@soegaard2 do you mean like (apply (namespace-variable-value fun #t #f ns) args)
?
It seems like this should be possible without having to sprinkle namespace declarations in the code. Isn’t the module name and function name sufficient for identifying the function?
Yes. (to the first)
No. A module can be instantiated multiple times.
I do think feeding the config (i.e. a mapping of symbol/string to function) to the Job Queue manager is a cleaner approach, but I’ll need to return to this idea later just to satisfy my curiosity. Thus far, I feel Racket has allowed me to do everything Ruby has more easily, so I have to think there is a nicer solution :slightly_smiling_face:
One option is to define your own version of define
, say, define-job
. Then (define-job (name . args) . body)
could expand into (begin (define (name . args) . body) (register-job 'name name))
, where register-job
stores the job in hash table.
Sure, I can envision a number of ways to make a nice UI for this, but I’m still curious about how to obtain a procedure from a symbol w/o any ahead-of-time cooperation i.e. w/o adding namespace decls.
I’m not sure of the implications of your statement that a module can be instantiated multiple times.
It’s rarely used - most programs don’t need to mess with namespaces. A namespaces has a “module registry” of the instantiated modules. When you create a new namespace, you can choose to attach existing instantiated modules. When you implement new languages, it is great to have that level of control.
@badkins it sounds like dynamic-require
is probably what you want
then you won’t have to worry about namespaces (most likely)
There are trickier issues if you want something that the dynamic-require
interface doesn’t provide (like access to lexical variables/unexported definitions/names without modules)
@samth I see that a symbol representing the function name can be provided as the 2nd arg of dynamic-require, but how would you convert a symbol representing the module name into something suitable for the 1st arg?
It seems that might bring me back to the need for namespaces
> ((dynamic-require 'racket 'map) add1 (list 1 2 3))
'(2 3 4)
That doesn’t seem to work with a module that isn’t installed as a package.
“reflection.rkt”
it’s really just what you’d put inside (require ...)
Yes, using “reflection.rkt” works. For this to be useable in a Job Queue, I’d probably need for the modules to be available in a standard way, so that would likely solve the problem.
i.e. I wouldn’t want to depend on relative directories, etc.
you can use (file "/foo/bar.rkt")
with absolute paths
Thanks @samdphillips & @samth - it does appear that dynamic-require
does the trick if that’s the approach I want.
I do think @soegaard2’s approach of basically registering handlers and names is probably a better design w/ less security risk than being able to invoke any function in the system.
Good to know I can do the equivalent of my Ruby approach w/ Racket though :slightly_smiling_face:
I’m working on a bit of assembly parsing again using some code @soegaard2 showed me a little while ago as a guide: (define (read-hex-digit)
(expect 'hex-digit hex-digit?)
(read-char))
(define (read-hex-digit*)
(if (hex-digit? (peek-char))
(cons (read-hex-digit) (read-hex-digit*))
'()))
(define (parse-number)
(cond
[(char=? (peek-char) #\$) (skip-char)
(hex-string->number (read-hex-digit*))]))
(define (hex-string->number str)
(string->number (format "#x~a" (list->string str))))
(with-input-from-string " $2a"
(λ ()
(skip-whitespace)
(parse-number)))
does my “hex-string->number” seem like a decent way to do the conversion? Seems like whenever I write code involving string manipulation like this to calculate things, it’s not the right way to go about it, so figure I’d best ask. Thanks :)
You do not need the #x
trick. There is an extra argument you can use to choose the base: (string->number "1f" 16)
gives 31
.
Oh man, I thought I had imagined that extra argument because I was looking for a #:base kwarg!
Thanks :)
Do you need “zero-or-more” hex digits after $, or do you need “one-or-more” ?
Good point! completely slipped my mind. I need a read-hex-digit+, don’t I?
I think so.
by the way, big thanks again for the nested-list example you wrote (that I’m using as a guide), it’s really helped me a great deal in understanding how to go about writing parsers this way.
Glad you found it useful.
I think that’s a good sign that it should be a keyword argument instead of a positional one
:thumbsup::skin-tone–3:
If you have a function that looks something up in a predetermined table like so: (define (lookup k)
(define the-hash
(hash 'a 1
'b 2))
(hash-ref the-hash k))
does that create a new copy of the hash every time the function is called? It seemed sensible to do this as a way of ensuring that the only way to look up values in said hash is via this function. However, if that means the table is created anew for every single lookup, I guess I’d better define it at the top level.
Thanks :)
@sydney.lambda that does create the hash every time. Here’s a version that accomplishes both of your goals: (define lookup
(let ([the-hash (hash 'a 1 'b 2)])
(lambda (k) (hash-ref the-hash k))))
@sydney.lambda Here are some additional ways to do what you want: (define the-hash
(local [(define the-hash (hash 'a 1 'b 2))]
(lambda (k) (hash-ref the-hash k))))
(splicing-let ([the-hash (hash 'a 1 'b 2)])
(define (lookup k) (hash-ref the-hash k)))
(splicing-local [(define the-hash (hash 'a 1 'b 2))]
(define (lookup k) (hash-ref the-hash k)
And alternatively, you can just use a plain-old module-level definition and simply not provide
the hash. This strategy hides the hash from other modules, but not from other code in the module. So it usually works best when your modules are reasonably small (<500 lines) and have well-defined boundaries (for example, by using provide
with contract-out
to make the module’s API explicit instead of using all-defined-out
).
But of those choices, I personally really like the splicing-local
one. Makes it easier to only use define
, lets you cut-and-paste definitions without changing anything, and avoids the question of “do I need let
, let*
, or letrec
?”
@samth that approach didn’t even cross my mind, but it makes perfect sense. @notjack I’ve never used anything involving splicing-… before, or local for that matter. Interesting! I’ll have to look into them. Thank you both :)
fundamentally @notjack’s approaches are all fancier versions of what I did, which as he points out has some advantages
I’ve always been more inclined to use let than define, same goes for if and (single-arm) cond. Looks like these will come in useful for making the switch to more Rackety forms.