Saturday, November 26, 2011

One-line here-documents

Many of the popular languages nowadays offer here-documents as a solution to making a string out of an unescaped text. This works great for large chunks of text but it becomes too cluttered and verbose when one wants a one-line string; in all languages I know that offer here-documents, one would have to use at least two (three?) lines to write one line of unescaped text.

For example, here's a one-line heredoc in Ruby:

my_string = <<HERE.strip
This "," that
HERE

# my_string => "This \",\" that\n"

The .strip right after <<HERE is necessary to avoid a nagging trailing newline that ruby adds to every heredoc.

So as you can see, it takes three lines - one to start the heredoc, one for the text, one to end the heredoc.

I have often wanted to have a "here-line" function to be able to do just that. As far as I know that is something completely impossible to write in any of those popular languages we're talking about - they don't give you control over the lexer.

Factor does:

USING: lexer multiline.private namespaces sequences ;

: rest-of-line ( -- seq )
lexer get [ line-text>> ] [ column>> ] bi tail "" like ;

SYNTAX: HERELINE:
lexer get skip-blank
rest-of-line suffix!
lexer get next-line ;

! sample usage ( can't be used in the same file as the SYNTAX: line above
! -- if using scratchpad, copy&paste above first, then this separately )
HERELINE: This "," that
dup . print

"This \",\" that"
This "," that

Tuesday, November 15, 2011

Emacs keybindings

Emacs keybindings

I'm an Emacs guy - I finally converted after years of resisting it.
I can't function if my keybindings aren't available to me.
C-a, C-e and C-k are especially important, and their use seem to be somewhat common in the unix world.

The Factor listener, however, does not implement those.
In fact, out of the box it uses C-a for selecting all text (C-x h in emacs), C-e for Factor's "edit" command (writing + and pressing C-e in stock Factor is equivalent to writing \ + edit and pressing <RET>), and C-k for killing an entire line (emacs' C-a C-k) or the whole text (emacs' C-x h C-w), I can't remember which.

I had made a patch for fixing this severe oversight, and mrjbq7 was kind enough to apply a modified version of it. Life was good. But then Slava reverted the patch, and I went back to a dysfunctional listener. The reason the patch was reverted was because it does not provide a real solution (which would be to make keybindings customizable), and it breaks muscle memory for those who were used to Factor's keybindings and that are not emacs users. I think that's fair - I still remember how it felt to not be an emacs user - everything seemed so emacs-centric!
Now that I'm a convert nothing ever feels emacs-centric enough :).

Not to worry, though, because qx opened my eyes to the fact that I could override the stock keybinding settings using my ~/.factor-rc file.

So I thought I'd post my ~/.factor-rc file whole in hopes that it will help a fellow emacs user (or a regular unix command line user).

The only thing I'm missing is better word navigation using C-← and C-→ (currently it stops on every boundary and does not behave like emacs' M-f and M-b). I'll see if I can fix that later.

Here it is, my unadultered ~/.factor-rc file:

USING: definitions editors.emacs io.pathnames multiline
namespaces parser ui.commands ui.gadgets.editors ui.gestures
ui.operations ui.tools.listener vocabs.hierarchy vocabs.refresh
;
QUALIFIED: editors

! ------------------------------------------------
! to get a trimmed USING: vocabs list, do this:
! - start factor normally
! - edit this file and comment all lines above this comment
! - comment in the auto-use line below
! - on the open Factor listener, run "~/.factor-rc" run-file
! - you can't really type ~, so please pre-expand it
! - resolve ambiguities if any restarts are thrown
! - copy and paste the suggested USING: here
! - comment out the auto-use line
! - go to #concatenative and thank qx for his patience
! auto-use ---------------------------------------

! setup emacs to work from Factor
"/usr/local/bin/emacsclient" \ emacsclient-path set-global

! quick troubleshooting - if the above does not work:
! make sure this has been done once:
! - sudo ln -s /Applications/Emacs.app/Contents/MacOS/bin/emacsclient /usr/local/bin/emacsclient
! make sure this is included in ~/.emacs.d/init.el:
! - (server-start)

! ----------
! setup the following keybindings as emacs has them:
! C-a, C-e, C-k

! basis/ui/gadgets/editors/editors.factor
! this adds C-k, C-a, C-e
editor "caret-motion" f {
{ T{ button-down } position-caret }
{ T{ key-down f f "LEFT" } previous-character }
{ T{ key-down f f "RIGHT" } next-character }
{ T{ key-down f { C+ } "LEFT" } previous-word }
{ T{ key-down f { C+ } "RIGHT" } next-word }
{ T{ key-down f f "HOME" } start-of-line }
{ T{ key-down f f "END" } end-of-line }
{ T{ key-down f { C+ } "HOME" } start-of-document }
{ T{ key-down f { C+ } "END" } end-of-document }
{ T{ key-down f { C+ } "k" } delete-to-end-of-line } ! +
{ T{ key-down f { C+ } "a" } start-of-line } ! +
{ T{ key-down f { C+ } "e" } end-of-line } ! +
} define-command-map

! basis/ui/tools/listener/listener.factor
! this substracts a flawed C-k
interactor "interactor" f {
{ T{ key-down f f "RET" } evaluate-input }
! { T{ key-down f { C+ } "k" } clear-editor } ! -
} define-command-map

! basis/ui/tools/operations/operations.factor
! these replace C-e with C-b to invoke the "edit" command
[ pathname? ] \ editors:edit-file H{
! { +keyboard+ T{ key-down f { C+ } "e" } } ! -
{ +keyboard+ T{ key-down f { C+ } "b" } } ! +
{ +primary+ t }
{ +secondary+ t }
{ +listener+ t }
} define-operation
! ---
[ definition? ] \ editors:edit H{
! { +keyboard+ T{ key-down f { C+ } "e" } } ! -
{ +keyboard+ T{ key-down f { C+ } "b" } } ! +
{ +listener+ t }
} define-operation

! end keybindings
! ----------

! uncomment and run Factor on the command line to sort USING: vocabs
! << manifest get pprint-manifest >>

A simpler continuation example

Here's a simpler example using call/cc that needs function-continuation isomorphism to work, and how to handle that need in Factor.
(let ((x (call/cc (lambda (k) k))))
(x (lambda (ignore) "hi")))

In the code above, x is a continuation the first time around, and then a function the second time around (read below for a walkthrough), and it gets called both times with the same syntax. Factor doesn't implement call for continuations, so we need to "fix" it:
QUALIFIED: kernel
USE: locals

: call ( quot/cont -- ) dup continuation?
[ continue-with ] [ kernel:call ] if ; inline

FROM: scratchpad => call ;

[let [ ] callcc1 :> x
[| ignore | "hi" ] x call ]

On the first line, x is bound to the continuation about to bind something to x.
The second line then calls x with a lambda, which causes x to continue, which bounds the lambda to x. Now we've just run the first line a second time.
When we're back at the second line again, x is the lambda, and now it gets passed itself as an argument, which causes it to simply return the string "hi".

Continuations have a reputation of being useless, but I believe this is because they are a bit complicated to code with and that they're rarely implemented as they should be (with a global CPS transform), thus making them slow in most languages that implement them as an afterthought.

Monday, November 14, 2011

From Lisp to Factor

Finally, the search is over.

I've been looking for the perfect programming language for years, and Factor is the one with the most potential to become that.

Before I found Factor I was into Lisp and Scheme, and I was in love with the parentheses. But that's just because I did not know Forth - if I had known it at the time I'd be working on a language that is a cross between Lisp and Forth.

Well, Factor is that language.

For my first post I will translate the good old yin yang trick from Scheme. I was not surprised to see that this works in Factor at all, since it seems that Factor actually copies "the stack and environment", much like the way schemers pretend call/cc works when explaining it to people. [1]

What yin yang does is it prints this ad infinitum:
@*@**@***@****@*****@****** ...

Here's the Scheme code:

(let* ((yin
((lambda (cc) (display #\@) cc)
(call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #\*) cc)
(call-with-current-continuation (lambda (c) c)))))
(yin yang))

And here's the Factor version with a safety trigger to prevent it from looping forever:

USE: locals ;

[let
0 :> counter!
[ ] callcc1 [| cc | "@" write counter
100 >
[ "force-stopped" throw ]
[ counter 1 + counter! ] if
cc ] call :> yin
[ ] callcc1 [| cc | "*" write cc ] call :> yang
yang yin continue-with ]


It will print something like this (without the newlines):

@*@**@***@****@*****@******@*******@********@
*********@**********@***********@************
@*************@**************@***************
@****************@*****************@*********
...

If you understand continuations you can read up on the docs for callcc1 and continue-with and immediately understand how this works exactly like the Scheme version (or how it can be made to do so if the safety trigger is removed).

[ ] callcc1 is the literal equivalent to (call/cc id) that you see above in its more verbose version.

continue-with is like Scheme's apply but only for continuations; it takes a continuation to be called, and one argument to be passed to it. Continuations and lambdas in Factor are not isomorphic, so we need a way to call continuations. arg k continue-with is equivalent to (k arg).

I'm using locals above to make it simpler to see, but the snippet would be shorter if I did away with the cc locals:

USE: locals ;

[let
0 :> counter!
[ ] callcc1 [ "@" write counter
100 >
[ "force-stopped" throw ]
[ counter 1 + counter! ] if
] call :> yin
[ ] callcc1 [ "*" write ] call :> yang
yang yin continue-with ]


[1] I believe only a few schemes implement call/cc with any copying at all. It is possible to get call/cc for free, i.e. its cost being the same as of a normal function call, and it is also possible that this way of implementing call/cc is actually easier than alternatives.