In Factor, a singleton is simply a predicate class whose predicate tests if the class is identical to itself. Singletons are defined like this:
SINGLETON: factorThat's it! No messy boilerplate to copy and paste, no subtle reasons why your singleton might be wrong in some corner case. Using the singleton, we can replace some of the less elegant parts of the Factor core code and implement things in a simpler way.
For instance, until now the core words
os
and cpu
have returned strings based on how the Factor binary is compiled. Soon, these strings will be parsed into whichever singleton they represent, allowing for generic dispatch. I wanted to have a cross-platform library for finding out basic hardware information about your computer, like how many cpus/cores, what speed, and how much RAM is in a machine. To do this without singletons, I had to redefine information already available as strings (the cpu and os words) as symbols. With singletons, this duplication can be removed. The same applies to the compiler code and loading libraries.Here is the way the core supported operating systems can be defined using singletons.
SINGLETON: winntNow we can dispatch on these to find the number of cores:
SINGLETON: wince
SINGLETON: macosx
SINGLETON: linux
UNION: windows winnt wince ;
HOOK: #cpus os ( -- n )For loading code, the current idiom is this:
M: macosx #cpus { 6 3 } sysctl-query-uint ;
M: winnt #cpus system-info SYSTEM_INFO-dwNumberOfProcessors ;
<< "alut" {Using singletons, we can shorten this to:
{ [ win32? ] [ "alut.dll" ] }
{ [ macosx? ] [ "/System/Library/Frameworks/OpenAL.framework/OpenAL" ] }
{ [ unix? ] [ "libalut.so" ] }
} cond "cdecl" add-library >>
"alut" {The library is assumed to be 'cdecl', but if it were 'stdcall' you could specify this by adding a "stdcall" string after the libary name, thus making a triple instead of a pair. The amount of boilerplate is reduced and the programmer can be more productive and write fewer bugs.
{ win32 "alut.dll" }
{ macosx "/System/Library/Frameworks/OpenAL.framework/OpenAL" }
{ unix "libalut.so" }
} add-library
The implementation of singleton is:
: define-singleton-class ( class -- )This generates code that looks like:
\ word swap
dup [ eq? ] curry define-predicate-class ;
PREDICATE: word winnt \ winnt eq? ;It makes a predicate class with a superclass of 'word' that you can dispatch on and only a single instance exists. Why are singletons so hard to define in some other languages?
2 comments:
Are you going to put SINGLETON: foo in wikipedia and watch it get removed? ;-)
In Dylan a singleton is a type for a specific instance of an object. They are the same as EQL types in CLOS. So you can dispatch on the 'type' for the integer 42 for example:
define method foo( x :: singleton(5))
...
end
So you could use symbols:
define method foo ( os :: singleton("win32"))
...
end
define method foo ( os :: singleton("linux"))
...
end
Called like:
foo("win32");
foo("linux");
(Note: pseudo dylan syntax as I've forgotten it :)
Post a Comment