From: Victor Kryukov
Subject: Using "internal" macros of a CL implementation
Date: 
Message-ID: <m2ac3fiuda.fsf@victor-kryukovs-computer.local>
Hello group,

I've the following question: I want to define macro case-string, which
behaves exactly like case, only using string-equal instead of eql.

I've looked up definition of case in SBCL[*], and realized that my
task is as simple as

(defmacro case-string (keyform &body cases)
  (case-body 'case keyform cases t 'string-equal nil nil nil))

My problem is with case-body - it's not imported by default, and I
don't know how to import it (I guess that my problem is with
understanding how packages work, strictly speaking). I also don't know
how to make my definition of case-string portable - I guess not all CL
implementations may use case-body internally.

And I don't want simply to copy-paste case-body definition in my source
files - it's seems to be not elegant, I would need to copy definition
of list-of-length-at-least-p as well, etc.

Is there any elegant solution?

Thanks,
Victor.

[*]: http://www.google.com/codesearch?q=+%22defmacro-mundanely+case%22+show:ZDN4RI9bILk:3jwtAwW1w8E:WJNTlSHN6zc&sa=N&cd=1&ct=rc&cs_p=http://distfiles.master.finkmirrors.net/sbcl-0.9.3-source.tar.bz2&cs_f=sbcl-0.9.3/src/code/macros.lisp#a0

From: Victor Kryukov
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <1162098523.854347.84760@e64g2000cwd.googlegroups.com>
Victor Kryukov wrote:

> I've looked up definition of case in SBCL[*], and realized that my
> task is as simple as
>
> (defmacro case-string (keyform &body cases)
>   (case-body 'case keyform cases t 'string-equal nil nil nil))
                           ^^^^

Should read
  (case-body 'case-string keyform cases t 'string-equal nil nil nil))
From: Pascal Bourguignon
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <87ejsr7l4x.fsf@thalassa.informatimago.com>
Victor Kryukov <···················@gmail.com> writes:

> Hello group,
>
> I've the following question: I want to define macro case-string, which
> behaves exactly like case, only using string-equal instead of eql.
>
> I've looked up definition of case in SBCL[*], and realized that my
> task is as simple as
>
> (defmacro case-string (keyform &body cases)
>   (case-body 'case keyform cases t 'string-equal nil nil nil))
>
> My problem is with case-body - it's not imported by default, and I
> don't know how to import it (I guess that my problem is with
> understanding how packages work, strictly speaking). I also don't know
> how to make my definition of case-string portable - I guess not all CL
> implementations may use case-body internally.
>
> And I don't want simply to copy-paste case-body definition in my source
> files - it's seems to be not elegant, I would need to copy definition
> of list-of-length-at-least-p as well, etc.
>
> Is there any elegant solution?

Il faut ce qu'il faut.

If you want your program to be portable, it must be stand alone (it
shouldn't depend on implementation specific stuff).  If you've found
in SBCL an implementation of case-string that works, then
copy-and-paste it in your program.

Or, make it a library asdf-installable on which you'll depend (and
other programs will too), and perhaps it'll become defacto standard
like slit-sequence.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Indentation! -- I will show you how to indent when I indent your skull!"
From: Ken Tilton
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <_k21h.4$Zq6.3@newsfe12.lga>
Victor Kryukov wrote:
> Hello group,
> 
> I've the following question: I want to define macro case-string, which
> behaves exactly like case, only using string-equal instead of eql.
> 
> I've looked up definition of case in SBCL[*], and realized that my
> task is as simple as
> 
> (defmacro case-string (keyform &body cases)
>   (case-body 'case keyform cases t 'string-equal nil nil nil))
> 
> My problem is with case-body - it's not imported by default, and I
> don't know how to import it (I guess that my problem is with
> understanding how packages work, strictly speaking). I also don't know
> how to make my definition of case-string portable - I guess not all CL
> implementations may use case-body internally.
> 
> And I don't want simply to copy-paste case-body definition in my source
> files - it's seems to be not elegant, I would need to copy definition
> of list-of-length-at-least-p as well, etc.
> 
> Is there any elegant solution?

COND.

kt

-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Smiling husband to scowling wife, New Yorker cartoon
From: Victor Kryukov
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <m264e22795.fsf@victor-kryukovs-computer.local>
Ken Tilton <·········@gmail.com> writes:

> Victor Kryukov wrote:
>> Hello group,
>>
>> I've the following question: I want to define macro case-string, which
>> behaves exactly like case, only using string-equal instead of eql.
>>
>> I've looked up definition of case in SBCL[*], and realized that my
>> task is as simple as
>>
>> (defmacro case-string (keyform &body cases)
>>   (case-body 'case keyform cases t 'string-equal nil nil nil))
>>
> COND.

Let me be more precise here. I do understand how to use COND to
achieve what I need. I just want more useful macro, so that

(let ((keyform "test"))
     (case-string keyform
        ("test1" 'test1)
        (("test2" "test3") 'test2-or-3)
        (("test" "test4") 'test-or-4)))

would evaluate to TEST-OR-4. Of course, you can do that simply by
using COND, as Ken suggested:

(cond
  ((string= keyform "test1") 'test1)
  ((member keyform '("test2" "test3") :test #'string=) 'test2-or-3)
  ((member keyform '("test" "test4") :test #'string=) 'test-or-4))

and is seems not that hard to write case-string macro which translates
first snippet to the second. I've just noticed that similar
functionality already existed in SBCL core - e.g. case is really
similar, it's only uses 'eql instead of string=, and is defined with
help of internal macro case-body; my question was how one can possibly
access such macros (even in non-portable way) to avoid duplicating
existing functionality.

Victor.
From: Pascal Costanza
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <4qku5vFnkj97U1@individual.net>
Victor Kryukov wrote:
> Ken Tilton <·········@gmail.com> writes:
> 
>> Victor Kryukov wrote:
>>> Hello group,
>>>
>>> I've the following question: I want to define macro case-string, which
>>> behaves exactly like case, only using string-equal instead of eql.
>>>
>>> I've looked up definition of case in SBCL[*], and realized that my
>>> task is as simple as
>>>
>>> (defmacro case-string (keyform &body cases)
>>>   (case-body 'case keyform cases t 'string-equal nil nil nil))
>>>
>> COND.
> 
> Let me be more precise here. I do understand how to use COND to
> achieve what I need. I just want more useful macro, so that
> 
> (let ((keyform "test"))
>      (case-string keyform
>         ("test1" 'test1)
>         (("test2" "test3") 'test2-or-3)
>         (("test" "test4") 'test-or-4)))
> 
> would evaluate to TEST-OR-4. Of course, you can do that simply by
> using COND, as Ken suggested:
> 
> (cond
>   ((string= keyform "test1") 'test1)
>   ((member keyform '("test2" "test3") :test #'string=) 'test2-or-3)
>   ((member keyform '("test" "test4") :test #'string=) 'test-or-4))
> 
> and is seems not that hard to write case-string macro which translates
> first snippet to the second. I've just noticed that similar
> functionality already existed in SBCL core - e.g. case is really
> similar, it's only uses 'eql instead of string=, and is defined with
> help of internal macro case-body; my question was how one can possibly
> access such macros (even in non-portable way) to avoid duplicating
> existing functionality.

In a non-portable way: Check in what packages they are defined, and 
access the right symbols in those packages. 'apropos helps to determine 
the package. Access to a symbol in a package is made by package:symbol, 
or package::symbol if it is not exported (but then it's also really not 
advisable to use it). There are more ways to access symbols in other 
packages via defpackage.

In a portable way: Create your own package with a low-level API. Make 
sure that the API is close to at least one or ore existing low-level 
APIs. Then you can provide different implementations depending on each 
implementation, and in some implementations simply make the existing one 
available through your own package. There exist numerous compatibility 
layers that do it like this, like ACL-Compat, LW-Compat, 
trivial-garbage, Closer to MOP, and various others. (For example, CLiki 
has a page for such compatibility layers.)

A necessary prerequisite is to learn and understand packages beforehand, 
either by studying a good tutorial or the HyperSpec/CLtL2 directly. It's 
also a very good idea to understand something about system construction, 
especially system definition utilities like asdf, mk-defsystem, and the 
likes.

I hope this helps.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Victor Kryukov
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <m21woq1syu.fsf@victor-kryukovs-computer.local>
> In a non-portable way: Check in what packages they are defined, and
> access the right symbols in those packages. 'apropos helps to
> determine the package. Access to a symbol in a package is made by
> package:symbol, or package::symbol if it is not exported (but then
> it's also really not advisable to use it). There are more ways to
> access symbols in other packages via defpackage.
>
> In a portable way: Create your own package with a low-level API. Make
> sure that the API is close to at least one or ore existing low-level
> APIs. Then you can provide different implementations depending on each
> implementation, and in some implementations simply make the existing
> one available through your own package. There exist numerous
> compatibility layers that do it like this, like ACL-Compat, LW-Compat,
> trivial-garbage, Closer to MOP, and various others. (For example,
> CLiki has a page for such compatibility layers.)

Thanks, Pascal; that's exactly what I was asking for, though I
probably wasn't very clear.

I am impressed to see a large number of very skilled persons (Bill,
Ken, Pascal & Pascal - to name just a few) in this group who spend
*significant* amount of their times explaining in great detail
different aspect of the language, often answering similar questions again
and again. I still fail to see why this group is sometimes considered to be
"hostile" to the newcomers.

Best regards,
Victor.
From: Ken Tilton
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <v3a2h.101$Rj2.98@newsfe10.lga>
Victor Kryukov wrote:
> I still fail to see why this group is sometimes considered to be
> "hostile" to the newcomers.

Nonsense. We just ate. Had a couple of nice juicy noobs in here just 
before you arrived, the big ones can take a couple of weeks to digest.

kt

-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Smiling husband to scowling wife, New Yorker cartoon
From: Victor Kryukov
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <m2psc6qu1r.fsf@victor-kryukovs-computer.local>
Ken Tilton <·········@gmail.com> writes:

> Victor Kryukov wrote:
>> I still fail to see why this group is sometimes considered to be
>> "hostile" to the newcomers.
>
> Nonsense. We just ate. Had a couple of nice juicy noobs in here just
> before you arrived, the big ones can take a couple of weeks to digest.

And of course, they never return... Those rare who survive become
noob-eaters - right after they learn how to write backquote
implementation themselves. Now I see why they say that "CL is a hard
language to learn" :)
From: Rob Warnock
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <1aWdnU36jc8q8NjYnZ2dnUVZ_vSdnZ2d@speakeasy.net>
Pascal Costanza  <··@p-cos.net> wrote:
+---------------
| Victor Kryukov wrote:
| > my question was how one can possibly access such macros
| > (even in non-portable way) to avoid duplicating existing functionality.
...
| In a portable way: Create your own package with a low-level API. Make 
| sure that the API is close to at least one or ore existing low-level 
| APIs. Then you can provide different implementations depending on each 
| implementation, and in some implementations simply make the existing one 
| available through your own package. There exist numerous compatibility 
| layers that do it like this, like ACL-Compat, LW-Compat, 
| trivial-garbage, Closer to MOP, and various others. (For example, CLiki 
| has a page for such compatibility layers.)
+---------------

Victor, here's a classic example of what Pascal's talking about:

    ;;; GETENV -- Mostly-portable code for accessing Unix
    ;;; (or Windows?) environment variables. Morphed very slightly
    ;;; from <URL:http://cl-cookbook.sourceforge.net/os.html>
    ;;; Copyright (c) 2002 The Common Lisp Cookbook Project 
    ;;; See: <URL:http://cl-cookbook.sourceforge.net/license.html>
    (defun getenv (name &optional default)
      (or
       #+CMU (cdr (assoc name ext:*environment-list* :test #'string=))
       #+Allegro (sys:getenv name)
       #+CLISP (ext:getenv name)
       #+ECL (si:getenv name)
       #+SBCL (sb-unix::posix-getenv name)
       #+LISPWORKS (lispworks:environment-variable name)
       default))


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: ···············@space.at
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <u1woch20q.fsf@space.at>
>>>>> "Rob" == Rob Warnock <····@rpw3.org> writes:

Slightly off-topic,

    Rob> [...] here's a classic example of [a portable
    Rob> implementation using implementation-specific internal
    Rob> functionality]

    Rob>     ;;; GETENV -- Mostly-portable code for accessing Unix
    Rob>     ;;; (or Windows?) environment variables. Morphed very slightly
                     ^^^^^^^^
    Rob>     ;;; from <URL:http://cl-cookbook.sourceforge.net/os.html>
    Rob>     ;;; Copyright (c) 2002 The Common Lisp Cookbook Project 
    Rob>     ;;; See: <URL:http://cl-cookbook.sourceforge.net/license.html>
    Rob>     (defun getenv (name &optional default)
    Rob>       (or
    Rob>        #+CMU (cdr (assoc name ext:*environment-list* :test #'string=))
    Rob>        #+Allegro (sys:getenv name)
    Rob>        #+CLISP (ext:getenv name)
    Rob>        #+ECL (si:getenv name)
    Rob>        #+SBCL (sb-unix::posix-getenv name)
    Rob>        #+LISPWORKS (lispworks:environment-variable name)
    Rob>        default))


This is SBCL 0.9.17, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
[..removed some chatter]
* (sb-unix::posix-getenv "HOME")

"h:/"

So it does work for SBCL on Windows XP.  The clisp I have here is
ancient (1999) and getenv was not exported then, but it does work
there too:
[1]> (lisp-implementation-version)
"1999-07-22 (July 1999)"
[2]>  (system::getenv "HOME")
"h:/"

Hope this helps.
                                 regards
                                    Roland
From: Ken Tilton
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <4w81h.65692$td3.50865@newsfe08.lga>
Victor Kryukov wrote:
> Hello group,
> 
> I've the following question: I want to define macro case-string, which
> behaves exactly like case, only using string-equal instead of eql.
> 
> I've looked up definition of case in SBCL[*], and realized that my
> task is as simple as
> 
> (defmacro case-string (keyform &body cases)
>   (case-body 'case keyform cases t 'string-equal nil nil nil))
> 
> My problem is with case-body - it's not imported by default, and I
> don't know how to import it (I guess that my problem is with
> understanding how packages work, strictly speaking).

So specify (whatever-cl-package::case-body....)

> I also don't know
> how to make my definition of case-string portable - I guess not all CL
> implementations may use case-body internally.

If you want portable, expand each case into a cond clause.

kt

-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Smiling husband to scowling wife, New Yorker cartoon
From: Bill Atkins
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <m2lkmy8pgn.fsf@weedle-24.dynamic.rpi.edu>
Victor Kryukov <···················@gmail.com> writes:

> Hello group,
>
> I've the following question: I want to define macro case-string, which
> behaves exactly like case, only using string-equal instead of eql.

Here is what I use for this sort of thing.  It could certainly be
cleaned up, but it's a start:

(defun build-case (test-fn var clauses &key errorp)
  (let ((x (gensym)))
    `(let ((,x ,var))
       (cond ,@(loop for (vals . code) in clauses
                     if (consp vals)
                     collect (append `((or
                                        ,@(loop for val in vals
                                                collect `(,test-fn ,x ,val))))
                                     code)
                     else
                     collect (append (when (and (eq vals t)
                                                (not errorp))
                                       `((,test-fn ,x ,vals)))
                                     code))
	     ,@(if errorp
                   `((t (error "~S fell through CASE; expected one of ~S"
                               ,var
                               ',(loop for (clause . rest) in clauses
                                       if (consp clause)
                                       append clause
                                       else collect clause)))))))))

(defmacro case-string-equal (var &body clauses)
  (build-case 'string-equal var clauses))

(defmacro ecase-string-equal (var &body clauses)
  (build-case 'string-equal var clauses :errorp t))

(defmacro case-string (var &body clauses)
  (build-case 'string= var clauses))

(defmacro ecase-string (var &body clauses)
  (build-case 'string= var clauses :errorp t))

CL-USER 5 > (macroexpand '(ecase-string-equal  "foob"
                            ("foo" 3)
                            (t 4)))
(LET ((#:X16625 "foob")) (COND ((STRING-EQUAL #:X16625 "foo") 3) ((STRING-EQUAL #:X16625 T) 4) (T (ERROR "~S fell through CASE; expected one of ~S" "foob" (QUOTE ("foo" T))))))
T
From: Bill Atkins
Subject: Re: Using "internal" macros of a CL implementation
Date: 
Message-ID: <m28xiyid7g.fsf@weedle-24.dynamic.rpi.edu>
Cleaned up a little:

(defun mklist (o)
  (if (consp o) o (list o)))

#-lispworks
(defmacro when-let ((var clause) &body body)
  `(let ((,var ,clause))
     (when ,var
       ,@body)))

#+lispworks
(import 'lispworks:when-let)

(defmacro generic-ecase (keyform test-fn &body clauses)
  (when (assoc t clauses)
    (error "GENERIC-ECASE already has a T clause"))
  `(generic-case ,keyform ,test-fn
     ,@clauses
     (t (error "~S fell through ECASE; expected one of ~S"
               ,keyform ',(loop for clause in clauses
                                if (consp (car clause))
                                appending (car clause)
                                else
                                appending (list (car clause)))))))

(defmacro generic-case (keyform test-fn &body clauses)
  (let ((x (gensym)))
    `(let ((,x ,keyform))
       (cond ,@(loop for (vals . code) in clauses
                     unless (eq vals t)
                     collect `((or ,@(loop for val in (mklist vals)
                                           collect `(,test-fn ,x ',val)))
                               ,@code))
	     ,@(when-let (t-clause (assoc t clauses))
                 (list t-clause))))))

(defmacro case-string-equal (keyform &body clauses)
  `(generic-case ,keyform string-equal ,@clauses))

(defmacro ecase-string-equal (keyform &body clauses)
  `(generic-ecase ,keyform string-equal ,@clauses))

(defmacro case-string (keyform &body clauses)
  `(generic-case ,keyform string= ,@clauses))

(defmacro ecase-string (keyform &body clauses)
  `(generic-ecase ,keyform string= ,@clauses))

---

Now you can use GENERIC-CASE as a macro on its own (the old version
used a function BUILD-CASE, so that a new macro had to be defined every
time you wanted a new kind of case).  So you can do:

(generic-case #\a char-equal
  (#\A 'foo))

(generic-ecase #\a char-equal
  (#\A 'foo))

or something similar.