badkins
2019-8-26 17:50:15

How do I get a procedure from either a symbol or a string representing the procedure name?


soegaard2
2019-8-26 17:52:10

If the procedures are in a namespace, then you use namespace-variable-value.


soegaard2
2019-8-26 17:52:39

If only a certain set of procedures are allowed/needed consider using a hash table instead.


badkins
2019-8-26 17:53:24

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.


badkins
2019-8-26 17:53:56

I should probably store both the module and function name to avoid having the job queue manager having to require everything.


badkins
2019-8-26 17:54:49

Your tip got me to the “Reflection & Security” section, so I can dig in to that a bit :slightly_smiling_face:


soegaard2
2019-8-26 17:55:14

In your use case I would probably use two hash tables: one from function to name and one the other way.


badkins
2019-8-26 17:57:17

I suppose that might be ok, it just requires “registering” the job functions ahead of time.


badkins
2019-8-26 18:01:29

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)


badkins
2019-8-26 18:02:57

i.e. if I have the module name and procedure name as symbols or strings


soegaard2
2019-8-26 18:04:32

You need to get the namespace “inside” a module first.


soegaard2
2019-8-26 18:05:17

I am looking for an example - can’t remember the exact incantation.


soegaard2
2019-8-26 18:06:57
(define-namespace-anchor here)
(define ns (namespace-anchor->namespace here))

soegaard2
2019-8-26 18:08:01

Then wrap namespace-variable-value in a parameterization of current-namespace.


badkins
2019-8-26 18:10:12

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.


badkins
2019-8-26 18:11:02

i.e. I would expect to be able to do something like (define fun (get-module-function 'Foo 'bar))


samdphillips
2019-8-26 18:12:10

There is also dynamic-require



soegaard2
2019-8-26 18:26:23

@badkins Fwiw you can use the fourth argument of namespace-variable-value instead of using current-namespace.


badkins
2019-8-26 18:27:54

@soegaard2 do you mean like (apply (namespace-variable-value fun #t #f ns) args) ?


badkins
2019-8-26 18:28:49

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?


soegaard2
2019-8-26 18:28:54

Yes. (to the first)


soegaard2
2019-8-26 18:30:16

No. A module can be instantiated multiple times.


badkins
2019-8-26 18:38:38

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:


soegaard2
2019-8-26 18:43:00

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.


badkins
2019-8-26 18:44:10

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.


badkins
2019-8-26 18:44:47

I’m not sure of the implications of your statement that a module can be instantiated multiple times.


soegaard2
2019-8-26 18:51:36

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.


samth
2019-8-26 18:51:41

@badkins it sounds like dynamic-require is probably what you want


samth
2019-8-26 18:52:37

then you won’t have to worry about namespaces (most likely)


samth
2019-8-26 18:53:42

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)


badkins
2019-8-26 18:57:31

@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?


badkins
2019-8-26 18:58:10

It seems that might bring me back to the need for namespaces


samth
2019-8-26 18:58:20
> ((dynamic-require 'racket 'map) add1 (list 1 2 3))
'(2 3 4)

badkins
2019-8-26 19:00:42

That doesn’t seem to work with a module that isn’t installed as a package.


badkins
2019-8-26 19:00:59

soegaard2
2019-8-26 19:03:13

“reflection.rkt”


samth
2019-8-26 19:04:05

it’s really just what you’d put inside (require ...)


badkins
2019-8-26 19:04:06

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.


badkins
2019-8-26 19:04:34

i.e. I wouldn’t want to depend on relative directories, etc.


samth
2019-8-26 19:04:54

you can use (file "/foo/bar.rkt") with absolute paths


badkins
2019-8-26 19:05:25

Thanks @samdphillips & @samth - it does appear that dynamic-require does the trick if that’s the approach I want.


badkins
2019-8-26 19:06:21

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.


badkins
2019-8-26 19:06:42

Good to know I can do the equivalent of my Ruby approach w/ Racket though :slightly_smiling_face:


sydney.lambda
2019-8-26 22:34:57

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 :)


soegaard2
2019-8-26 22:38:49

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.


sydney.lambda
2019-8-26 22:39:40

Oh man, I thought I had imagined that extra argument because I was looking for a #:base kwarg!


sydney.lambda
2019-8-26 22:39:47

Thanks :)


soegaard2
2019-8-26 22:40:29

Do you need “zero-or-more” hex digits after $, or do you need “one-or-more” ?


sydney.lambda
2019-8-26 22:41:30

Good point! completely slipped my mind. I need a read-hex-digit+, don’t I?


soegaard2
2019-8-26 22:41:37

I think so.


sydney.lambda
2019-8-26 22:53:14

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.


soegaard2
2019-8-26 22:53:44

Glad you found it useful.


notjack
2019-8-26 23:02:36

I think that’s a good sign that it should be a keyword argument instead of a positional one


samdphillips
2019-8-26 23:26:48

:thumbsup::skin-tone–3:


sydney.lambda
2019-8-27 00:03:28

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 :)


samth
2019-8-27 00:39:22

@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))))


notjack
2019-8-27 00:46:31

@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).


notjack
2019-8-27 00:49:50

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?”


sydney.lambda
2019-8-27 02:20:55

@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 :)


samth
2019-8-27 02:21:50

fundamentally @notjack’s approaches are all fancier versions of what I did, which as he points out has some advantages


sydney.lambda
2019-8-27 02:26:38

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.