From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Ian Price Newsgroups: gmane.lisp.guile.user Subject: Higher order modules: a first stab Date: Fri, 09 Mar 2012 13:51:51 +0000 Message-ID: <87obs5991k.fsf@Kagami.home> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: dough.gmane.org 1331301163 10626 80.91.229.3 (9 Mar 2012 13:52:43 GMT) X-Complaints-To: usenet@dough.gmane.org NNTP-Posting-Date: Fri, 9 Mar 2012 13:52:43 +0000 (UTC) To: guile-user@gnu.org Original-X-From: guile-user-bounces+guile-user=m.gmane.org@gnu.org Fri Mar 09 14:52:41 2012 Return-path: Envelope-to: guile-user@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1S60FL-0003zz-Bv for guile-user@m.gmane.org; Fri, 09 Mar 2012 14:52:39 +0100 Original-Received: from localhost ([::1]:52619 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S60FK-0004xN-Nl for guile-user@m.gmane.org; Fri, 09 Mar 2012 08:52:38 -0500 Original-Received: from eggs.gnu.org ([208.118.235.92]:59558) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S60FD-0004vy-GO for guile-user@gnu.org; Fri, 09 Mar 2012 08:52:35 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1S60Et-0001QG-8Q for guile-user@gnu.org; Fri, 09 Mar 2012 08:52:31 -0500 Original-Received: from plane.gmane.org ([80.91.229.3]:35455) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S60Es-0001Px-TV for guile-user@gnu.org; Fri, 09 Mar 2012 08:52:11 -0500 Original-Received: from list by plane.gmane.org with local (Exim 4.69) (envelope-from ) id 1S60En-0003TF-Dm for guile-user@gnu.org; Fri, 09 Mar 2012 14:52:05 +0100 Original-Received: from host31-53-168-239.range31-53.btcentralplus.com ([31.53.168.239]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Fri, 09 Mar 2012 14:52:05 +0100 Original-Received: from ianprice90 by host31-53-168-239.range31-53.btcentralplus.com with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Fri, 09 Mar 2012 14:52:05 +0100 X-Injected-Via-Gmane: http://gmane.org/ Original-Lines: 117 Original-X-Complaints-To: usenet@dough.gmane.org X-Gmane-NNTP-Posting-Host: host31-53-168-239.range31-53.btcentralplus.com User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.3 (gnu/linux) Cancel-Lock: sha1:FTqmr21qCAbSdTPSP2OMfxIrPAU= X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 80.91.229.3 X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-user-bounces+guile-user=m.gmane.org@gnu.org Original-Sender: guile-user-bounces+guile-user=m.gmane.org@gnu.org Xref: news.gmane.org gmane.lisp.guile.user:9334 Archived-At: Hi guilers, I've been musing over the concept of higher order modules (modules parameterized by other modules) in the back of my mind, and am occasionally making steps towards implementing it in guile. I think that with guile's first class modules all the machinery is there, it's just a question of figuring out the best way to do it. The advantage for users of guile is that it provides a different, and hopefully useful, form of code reuse. Here is the first step towards it that I made. ;;; higher order modules for guile (define-syntax-parameter provide ; an "export" for `module' forms (lambda (stx) (syntax-violation 'provide "provide used outside a module form" stx))) (define-syntax module (lambda (stx) (syntax-case stx () ((module (params ...) body ...) (with-syntax (((tmp ...) (generate-temporaries #'(params ...)))) #'(lambda (tmp ...) (define fresh (make-fresh-user-module)) (module-use! fresh (resolve-interface '(guile))) (save-module-excursion (lambda () (set-current-module fresh) (let ((export-alist '())) (syntax-parameterize ((provide (syntax-rules () ((provide foo bar (... ...)) (set! export-alist (append (list (cons 'foo foo) (cons 'bar bar) (... ...)))))))) (define params tmp) ... body ... (map (lambda (pair) (let ((var (car pair)) (val (cdr pair))) (module-define! fresh var val))) export-alist))))) fresh)))))) ;; example module (define stream-utils (module (stream-car stream-cdr) (define (stream-drop s n) (if (zero? n) s (stream-drop (stream-cdr s) (- n 1)))) (define (stream-take s n) (if (zero? n) '() (cons (stream-car s) (stream-take (stream-cdr s) (- n 1))))) (provide stream-drop stream-take))) ;; first stream implementation ;; Stream a = Stream b (b -> a) (b -> Stream a) (import (rnrs records syntactic)) (define-record-type stream (fields val this next)) (define (stream-car stream) ((stream-this stream) (stream-val stream))) (define (stream-cdr stream) ((stream-next stream) (stream-val stream))) (define (constant-stream k) (define (next k) (make-stream k identity next)) (next k)) (define naturals (letrec ((next (lambda (current) (make-stream (+ 1 current) identity next)))) (next -1))) (define stream-module (stream-utils stream-car stream-cdr)) (define stream-take (module-ref stream-module 'stream-take)) (stream-take naturals 10) ;;(0 1 2 3 4 5 6 7 8 9) ;; second stream implementation (use-modules ((ice-9 streams) #:renamer (symbol-prefix-proc 's:))) (define naturals2 (s:make-stream (lambda (x) (cons x (1+ x))) 0)) (define stream-module2 (stream-utils s:stream-car s:stream-cdr)) (define stream-take2 (module-ref stream-module2 'stream-take)) (stream-take2 naturals2 10) Currently, parameterized modules are functions that return a module, and functions are retrieved by module-ref. This is fine for a proof of concept, but hopefully I'll have something better integrated soon. Thanks to wingo for suggesting the current method of providing exports -- Ian Price "Programming is like pinball. The reward for doing it well is the opportunity to do it again" - from "The Wizardy Compiled"