Lovely! I was thinking along these lines, but you hit it out of the park. 
Sounds like you must be avoiding some really important task!

John

> On Oct 14, 2017, at 11:31, Ryan Culpepper <ry...@ccs.neu.edu> wrote:
> 
> On 10/14/2017 05:01 AM, George Neuner wrote:
>> On 10/14/2017 3:00 AM, Jack Firth wrote:
>>> 
>>>    So is there a way ... from normal code ... to get at the locals of
>>>    functions higher in the call chain?  Or at least the immediate
>>>    caller?
>>>    Some reflective capability that I haven't yet discovered?
>>> 
>>> 
>>> I'm not sure if there's a way to do that, but I'm wondering if what you 
>>> want to do can be achieved more simply with plain functions and a very 
>>> small macro wrapper. In particular, I suspect putting too much logic in the 
>>> macro is what led you to eval which is the root of all evil. From what I 
>>> can tell there shouldn't be any need at all for eval or any sort of dynamic 
>>> runtime compilation to do things like what you're describing. Could you 
>>> give a few more details about your use case? Ideally with some example code 
>>> illustrating the problem?
>> Basically, this is a sort of Unix at-like function for flexible scheduling.  
>> It takes an expression containing dates, times, certain keywords and 
>> arbitrary numeric expressions, and it produces seconds since the epoch.  
>> Code is attached - hope it survives posting to the list.  It should be 
>> runnable as is.
>> What led me to eval originally was wanting to reference arbitrary 
>> functions/variables from the runtime environment.  I'd like to be able to say
>> things like:
>>    (let [(y 42)] (schedule (at now + y mins) ...))
>> and similar involving top-level defined functions [which already works with 
>> eval].
>> I know that eval is not needed if I generate inline code rather than having 
>> the macro invoke a normal function.  But that is complicated by having to 
>> deal with free form expressions: they can have internal references between 
>> things which are not necessarily adjacent.  It is doable, but at some 
>> expense and not very cleanly.
>> I started out going the "compile" route - just generating inline code.  But 
>> as more functionality was added, that became unwieldy. So I switched to a 
>> runtime function. Right now the assoc list code is overly complex [so please 
>> ignore it] - it is there as a debugging tool until I get everything working 
>> exactly right.
> 
> Your example above shows that you want the `at` macro to accept a mixture of 
> Racket expressions and syntax that you interpret. If you want to accept 
> Racket expressions, you must leave them as syntax objects. Once you flatten 
> them with `syntax->datum`, there is *no way* to *correctly* recover their 
> meaning.
> 
> Here's my attempt to adapt the HtDP design recipe to a scaled-down version of 
> this problem.
> 
> First, let's think about the syntax (grammar) that `at` should accept. Let's 
> call it a Time. What are some reasonable Times?
> 
>  now
>  now + 30     ;; means 30 seconds
>  today
>  tomorrow + 20 mins
>  now + x mins + (/ ms 1000) secs
>  "2017-10-14 1:17:25" + 30 mins
> 
> But there are also some terms that are nonsense as Times:
> 
>  12
>  today + tomorrow
>  now mins
> 
> Let's say (as a place to start) that a Time consists of an absolute reference 
> point (like now or today) and some number of offsets. Here's a grammar:
> 
>  ;; Time = TimeBase {+ TimeExt}*
>  ;; TimeBase = now | today | String[Date]
>  ;; TimeExt = Expr[Real] MaybeUnit
>  ;; MaybeUnit = <nothing> | secs | mins
> 
> Let's call the *meaning* of a Time a TimeVal, and let's represent it as a 
> real number of seconds, using the same epoch as (current-seconds).
> 
>  ;; A TimeVal is a real number of seconds
>  ;; using the same epoch as (current-seconds)
> 
> The meaning of TimeBase is also a TimeVal. The meanings of TimeExt and 
> MaybeUnit are both Real (they are duration and durations scales; they have no 
> epoch).
> 
> Now to translate that plan to Racket.
> 
> First, turn keywords like `now` into "illegal use" macro definitions:
> 
>  (begin-for-syntax
>    (define (at-keyword stx)
>      (raise-syntax-error #f "illegal use of `at` keyword" stx)))
>  (define-syntax now at-keyword)
>  (define-syntax today at-keyword)
>  (define-syntax secs at-keyword)
>  (define-syntax mins at-keyword)
> 
> Then define syntax classes for the nonterminals in the grammar. I'll also 
> define an `expr` attribute to compute the meaning of each term. For example, 
> the syntax class for TimeBase is
> 
>  (begin-for-syntax
>    (define-syntax-class TimeBase
>      #:attributes (expr) ;; expression of TimeVal
>      #:literals (now today)
>      (pattern now
>               #:with expr #'(current-seconds))
>      (pattern today
>               #:with expr #'(today-fun))
>      (pattern s:str
>               #:with expr #'(parse-date-string s))))
> 
> To avoid complicating the syntax class and also to avoid making the expansion 
> bigger than necessary, factor run-time behavior out into helper functions, 
> like `today-fun`:
> 
>  ;; today-fun : -> TimeVal
>  (define (today-fun)
>    (define today (current-date))
>    (find-seconds 0 0 0 (date-day today) (date-month today) (date-year today)))
> 
> Here's the syntax class for TimeExt. It uses `expr/c` to make sure the given 
> Racket expression actually produces a number.
> 
>  (begin-for-syntax
>    (define-splicing-syntax-class TimeExt
>      #:attributes (expr) ;; expression of Real
>      (pattern (~seq (~var r (expr/c #'real?)) u:MaybeUnit)
>               #:with expr #'(* r.c u.expr))))
> 
> Finally, `at` just wraps the work done by the syntax classes up as a macro:
> 
>  ;; (at Time) -> TimeVal
>  (define-syntax at
>    (syntax-parser
>      [(_ t:Time) #'t.expr]))
> 
> See the attached file for some of the missing pieces, like testing. It should 
> be possible to scale this approach up to the Time grammar you want.
> 
> ----
> 
> The other question you should ask is whether the benefit of the macro is 
> worth the cost to write it. Is `(at today + 30 mins + x secs)` that much 
> better than `(+ (today) (mins 30) x)`, which you could support with just a 
> few function definitions?
> 
> Ryan
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to racket-users+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
> <at.rkt>



-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to