John Benediktsson: What can you get from 1, 2, 3, 4, +, -, *, /, and ^?
Recently, a Haskell program was posted that computed all possible combinations of the numbers "1, 2, 3, and 4" and the operators "+, -, *, /, and ^" (allowing the operators to be repeated, but not the numbers). It's a fun little problem, and I thought it might be a good example for iterative development in Factor using the listener. First, some vocabularies that we'll be using. ( scratchpad ) USING: arrays continuations kernel math We can calculate all-permutations of the numbers (for a total of 4!, or 24): And, similarly, we can find all possible selections of three operations (for a total of 53, or 125): Now, we can compute the cartesian-product to produce all possible pairings of the two sequences (for a total of 24 × 125, or 3000): ( scratchpad ) cartesian-product concat You can inspect the list by clicking on it in the listener, printing it out (e.g., " ( scratchpad ) dup first . Use concat-as to make all the entries quotations (so they can be called). ( scratchpad ) [ [ ] concat-as ] map We can then try calling the first element to make sure it produces the right result: ( scratchpad ) dup first dup . call . Let's call each quotation, creating an association list where the key is the quotation and the value is the result: ( scratchpad ) [ dup call 2array ] map Whoops, some of the formulas produce division-by-zero errors. We can use continuations to recover (storing a ( scratchpad ) [ [ dup call ] [ drop f ] recover 2array ] map Each element of the resulting sequence is a pairing of a quotation and a result: ( scratchpad ) dup first . We can see how many unique results (including ( scratchpad ) dup values unique assoc-size . You could calculate the 10 most common results using sorted-histogram. It turns out "1" is the most common result: ( scratchpad ) dup values sorted-histogram reverse 10 head . Some other things you might try:
Update: it was pointed out by Kevin Reid (the author of the Haskell version) that I'm missing left-associative operators. I think the only modification that is required is to add "swapped" versions of the operators to the possible choices: ( scratchpad ) USE: quotations This produces 103, or 1,000, possible operations. When combined with the original 24 permutations of Chris Double: Dependent Types in ATS
Dependent types are types that depend on the values of expressions. ATS uses dependent types and in this post I hope to go through some basic usage that I’ve learnt as I worked my way through the documentation, examples and various papers. While learning about dependent types in ATS I used the following resources:
Most of the examples that follow are based on examples in those resources. Sorts and TypesSome dependently typed languages allow types to depend on values expressed in the language itself. ATS provides a restricted form of dependent types. The expressions that types can depend on are in a restricted constraint language rather than the full language of ATS. This constraint language is itself typed and to prevent confusion they call the types in that language ‘sorts’. References to ‘sort’ in this post refer to types in that constraint language. References to ‘type’ refers to types in the core ATS language itself. Here is an example of the difference. The following is an example of a function that takes two numbers and returns the result of them added together. This is in a hypothetical dependently typed language where types can depend on language values itself:
Here the result type is the exact integer type of the two numbers added together. A mistake in the body of the code that resulted in anything but the sum of the two numbers would be a type error. In ATS this function would look like:
Notice here the introduction of Having a restricted constraint language for type values simplifies the type checking process. All computations in this language are pure and have no effects. Sorts and functions in the language must terminate. This avoids infinite loops and exceptions during typechecking. For more information on the reasoning behind restricted dependent types see Dependent Types in Practical Programming and other papers at the ATS website. In ATS documentation the restricted constrainted language is called the ‘statics’ of ATS and is documented in chapter 5 of the ATS user guide. Simple ListsBefore I get into datatypes that use dependent types, I’ll do a quick overview of non-dependent types for those not familiar with ATS syntax. A basic ‘list’ type that can contain integers can be defined in ATS as:
With this type defined a list of integers can be created using the following syntax:
Functions that operate over lists can use pattern matching to deconstruct the list:
A complete example program is in dt1.dats (html). This can be built with the command:
Note the Polymorphic ListsInstead of a list of integers we might want lists of different types. A list that is polymorphic can be defined using:
Here the list can hold elements that are of any size. This is what the
The This example adds a definition for The complete example program is in dt2.dats (html). The example program has the following code:
Note that the length of list We can encode the length of a list as part of the type to get a compile error if an attempt is made to Dependently Typed ListsThe following datatype definition defines a polymorphic list of length
It is very similar to the previous polymorphic list definition except for the additional The implementation of the functions shown previous for this new list type are function templates as they were in the previous example. They also have the additional parameter for the length value:
Notice the type of the return value of
Similarly the type of the return value of
It is now a compile error to pass lists of different lengths to The complete example program is in dt3.dats (html). FilterIt is not always possible to know the exact length of a result list which could make encoding the type problematic. A
In this erroneous example the result is defined as a list of length
This unfortunately means that the typechecker won’t detect some examples of erroneous code. We’d like this to fail to compile if it results in a result list which is larger than the original list which should be impossible:
The solution to this is to limit the existential type in the result to be all natural numbers less than or equal to the length of the input list:
Note the The complete example program is in dt4.dats (html). DropThe implementation of a
Like
This version will typecheck correctly. It will give a compile time error if the implementation incorrectly produces a list that is not exactly the expected size. It will also be a compile time error if the given This is done by making The complete example program is in dt5.dats (html). Lists that depend on their valuesThe previous list examples were datatypes that were dependant on the size of the list. It’s also possible to depend on the value stored in the list itself. The following is the definition for a list that can only hold even numbers:
The
The complete example program is in dt6.dats (html). Another example of a list that depends on its values might be a list where all elements must be less than 100 and in order. It should be a type error to construct an unordered list.
An Out of order construction like the following will be a type error:
Whereas this is fine:
The complete example program is in dt7.dats (html). Red-Black TreesThe paper Dependently Typed Datastructures has an example of using dependently typed datatypes to implement red-black trees. The paper uses the language DML. I’ve translated this to ATS in the example that follows. A red-black tree is a balanced binary tree which satisfies the following conditions:
These constraints can be defined in the red-black tree datatype ensuring that the tree is always balanced and correct as per the conditions above. It becomes impossible to implement functions that produce a tree that is not a correct red-black tree since it won’t typecheck. This can be defined as:
The type The constructor, The constructor The constructor The type for a function that inserts a key into the tree can now be defined as:
This means our implementation of When inserting a node into the tree we can end up with a tree where the red-black tree conditions are violated. A function
The type of the The use of The
The complete example program is in dt8.dats (html). For another example of red-black trees in ATS see the funrbtree example from the ATS website. Linear constraintsThe constraints generated by the dependent types must be ‘linear’ constraints. An example of a linear constraint is the definition of ‘list_append’ earlier:
The result type containing
The Closing notesAlthough I create my own There’s a lot more that can be done with ATS and dependent types. For more examples see the papers mentioned at the beginning at throughout this post. The paper Why Dependent Types Matter is also useful reading for more on the topic. John Benediktsson: Floating-point Fractions
Recently, I wanted a way to convert floating-point numbers into fractions using Factor. To do this (with any hope of being correct), I spent some time understanding how floating-point numbers are represented. Two useful resources about floating-point numbers are an article entitled "What Every Computer Scientist Should Know About Floating-Point Arithmetic" and a website called The Floating-Point Guide. Basic floating-point numbers are specified using a sign bit, an exponent, and a mantissa. Aside from some special numbers (e.g., +Inf, -Inf, NaN) and denormal numbers, the value of a floating-point can be calculated using the formula: (-1)sign × 2exponent - exponent bias × 1.mantissa We will be working with double precision floating point values (e.g., 64-bit values): To extract the sign, exponent, and mantissa bits is fairly easy: USING: kernel math math.bitwise math.functions ; We are not going to support special values, so we throw an error if we encounter one: : check-special ( n -- n ) Converting to a ratio (e.g., numerator and denominator) is just a matter of computing the formula (with some special handling for denormal numbers where the exponent is zero): : float>ratio ( n -- a/b ) You can see this in action: ( scratchpad ) 0.5 float>ratio . John Benediktsson: Hello, web!
One thing that surprises many people when they come to Factor, is that a lot of the Factor infrastructure (main site, planet, pastebin, documentation, and wiki) is written in Factor, and runs on a Factor web server. The Factor web server is very capable, supporting static files, CGI scripts, SSL authentication, session management, and dynamic web pages. Some of the vocabularies that are involved:
Hello, world!This is a simple application that returns a plain text page that says "Hello, world!". Our web application is structured into a dispatcher (our "main responder"), an action, and words to create and run the web server. USING: accessors furnace.actions html.forms http http.server Run the code by calling TemplatesTo begin experimenting with templates, lets change the logic to include a form where a name can be provided. We will create a Chloe template file. Let's create a <?xml version='1.0' ?> Now, modify the USE: formatting When you navigate to http://localhost:8080, you will see a simple form prompting you to type in a name. After submitting the form, you will see a customized response depending on the name provided. Form ValidationIt is frequently useful to validate parameters that are submitted via forms (e.g., for numbers, e-mail addresses, ranges, required or optional, etc.). To support this, we need to add validation logic for every parameter desired (using words from the validators vocabulary). In this case, the name should be a required parameter: USE: validators Next, wrap the dispatcher in an <alloy>, which provides support for session-persistence, form validation, and database persistence. USE: furnace.alloy If you navigate to the website now, and don't provide a Other tipsThere is a development? symbol that can be set to Malu has a nice tutorial on Github about building a blog application in Factor. All of the Factor websites (as well as some nice examples like a "counter", "todo list", "tiny url", and "ip address") are in Jeremy Hughes: Old blog: Map in Java
(Found in my years old defunct blog. Less relevant now that Java is getting lambdas.)
Scheme
(map fn lst ...)
;; ==> list
Java has no map. Enhanced for isn't adequate for the following reasons:
ExamplesSimple one list mapping
Scheme
(define squares (list 1 4 9 16))
(define (sqrts nums)
(map sqrt nums))
(sqrts squares)
;; ==> (1 2 3 4)
Java
/* No generics for clarity. Assume also the existance of a static
method `toList' that takes an Array and returns an ArrayList. */
List squares = toList(new Integer[] {1, 4, 9, 16});
List sqrts(List nums) {
List out = new ArrayList();
for (Integer i : nums)
out.add(Math.sqrt(i));
return out;
}
sqrts(squares);
// ==> [1, 2, 3, 4]
Multiple list mapping
Scheme
(define even (list 2 4 6 8))
(define odd (list 1 3 5 7))
(define (sums . lists)
(apply map + lists))
(sums even odd)
;; ==> (3 7 11 15)
Java
/* Assume the existance of a static method `isSameLength' that tests
whether all Lists in an array are the same length. */
List even = toList(new Integer[] {2, 4, 6, 8});
List odd = toList(new Integer[] {1, 3, 5, 7});
List sums(List... lists) {
List out = new ArrayList();
if (!isSameLength(lists))
throw new RuntimeException("Lists must have same length.");
int numargs = lists.length;
int length = lists[0].size();
for (int i = 0; i < length; i++) {
int val = 0;
for (int k = 0; k < numargs; k++)
val += lists[k].get(i);
out.add(val);
}
return out;
}
sums(even, odd);
// ==> [3, 7, 11, 15]
The problem with this is that it isn't generalised. Map in JavaFirst we'll need a something that acts as a first-class function.
Java
public interface Proc {
public Object apply(Object... args);
}
Next, a static map method.
Java
public static List map(Proc proc, List... lists) {
List out = new ArrayList();
if (!isSameLength(lists))
throw new RuntimeException("`map' requires lists of identical length");
int numargs = lists.length;
int length = lists[0].size();
for (int i = 0; i < length; i++) {
Obj[] args = new Obj[numargs];
for (int k = 0; k < numargs; k++)
args[k] = lists[k].get(i);
out.add(proc.apply(args));
}
return out;
}
Now, the multiple lists example again.
Java
List even = toList(new Integer[] {2, 4, 6, 8});
List odd = toList(new Integer[] {1, 3, 5, 7});
Proc sum = new Proc() {
public Object apply(Object... args) {
return (Integer)args[0] + (Integer)args[1];
}
};
List sums(List... lists) {
return map(sum, lists);
}
sums(even, odd);
// ==> [3, 7, 11, 15]
Much shorter, but with some limitations:
Parametrized map First,
Java
public interface Proc1<R, A> {
public R apply(A arg);
}
public interface Proc2<R, A, B> {
public R apply(A argA, B argB);
}
public interface ProcN<R, A, B,..., N> {
public R apply(A argA, B argB,..., N argN);
}
And a parameterized
Java
public static <R, A, X extends List<R>> X map(Proc1<R, A> proc, X out, List<A> list) {
for (A a : list)
out.add(proc.apply(a));
return out;
}
public static <R, A, B, X extends List<R>> X map(Proc2<R, A, B> proc,
X out, List<A> listA, List<B> listB) {
if (!isSameLength(listA, listB))
throw new RuntimeException("`map' requires lists of identical length.");
int length = listA.size();
for (int i = 0; i < length; i ++)
out.add(proc.apply(listA.get(i), listB.get(i));
return out;
// And so on up to `map<R, A, B,..., N, X>.
The example would now be:
Java
List even = toList(new Integer[] {2, 4, 6, 8});
List odd = toList(new Integer[] {1, 3, 5, 7});
Proc2<Integer, Integer, Integer> sum =
new Proc<Integer, Integer, Integer>() {
public Integer apply(Integer argA, Integer argB) {
return argA + argB;
}
};
List sums(List listA, List listB) {
return map(sum, new ArrayList(), listA, listB);
}
sums(even, odd);
// ==> [3, 7, 11, 15]
The cost of parameterizing The obvious limitation of the generic implementation is that arbitrary numbers of arguments are no longer supported in Reduce
Java
public static <R, A> R reduce(Proc2<R, R, A> proc, List<A> list, R initial) {
int size = list.size();
R out = initial;
for (A a : list)
out = fun.apply(out, a);
return out;
}
List nums = toList(new Integer[] {1, 2, 3, 4});
Integer squareSum(List list) {
return reduce(new Proc2<Integer, Integer, Integer>() {
public Integer apply(Integer a, Integer b) {
return a + (b * b);
}
}, list, 0);
}
squareSum(nums);
// ==> 30
Chris Double: Experimental Playback Statistics for HTML Video and Audio
Now that HTML video is getting more usage there have been requests for statistics during playback so performance can be measured from JavaScript. This has come up a few times on the WHATWG mailing list. I raised bug 580531 to add some additional data to media and video DOM elements to provide this information. The patch in that bug adds two attributes to the DOM HTMLVideoElement object, both prefixed by ‘moz’ to prevent name clashes as they are experimental:
mozDecodedFrames is an incrementing count each time a frame is decoded and ready to be displayed. mozDroppedFrames is incremented each to a frame is dropped to keep playback going at the correct frame rate. These can be used to compute the current framerate that the video is being decoded at, and the framerate that it should be decoding at by combining the two counts. This will give client JavaScript code an indication if the hardware is able to play the video. Another requested feature is information on the download rate. Currently the Firefox implementation of HTML video and audio uses a non-standard extension to ‘progress’ events. We provide information attached to the event that gives the current byte position in the media file as it downloads, and the total bytes available. This was actually required by the WHATWG spec at one point but it changed a while back and I don’t think any other browser implements it. This has been used in experimental pages to compute things like download rate and we use it in our own controls implementation to display a progress bar as we didn’t implement the ‘buffered’ attribute. Support for ‘buffered’ has now landed so we want to remove the non-standard ‘progress’ information to be spec compliant. To address the needs of those using the progress data for bandwidth calculation I’ve added two attributes to HTMLMediaElement:
mozDownloadRate is an estimate, in bytes per second, of the current download rate of the media. If not enough information has been downloaded for a reliable estimate this will be NaN. mozDecodeRate provides the rate at which decoding is occurring. In the patch this is the length of the video divided by the duration and remains constant. Whether this gets landed is yet to be determined but I think the information is useful and I’d be interested in any feedback on the data I’ve decided to include. There is some feedback in the bug from the patch review already and there’s plenty of time between now and when the Firefox 4 rush is over to look over ideas of what could be included, removed or changed. I posted about the proposed additions in the WHATWG mailing list and there are some responses in that thread. John Benediktsson: Calculator with GUI
Update: Kyle Cordes has made some nice refactoring to avoid the "code smell" of passing global variables around while building the gadgets. I started playing around with the Factor GUI framework recently. The documentation is very detailed, but sometimes it is nice to have simple examples to learn from. I thought it would be fun to build a simple calculator application. A teaser of what it will look like when we are done: ![]() First, some imports and a namespace. Note: we have to specifically importUSING: accessors colors.constants combinators.smart kernel fry change-model from the models vocabulary, since it might conflict with an accessor.Factor user interface elements are called gadgets. Many of them support being dynamically updated by being connected to models. Each model maintains a list of connections that should be updated when the value being held by the model changes. The ModelOur TUPLE: calculator < model x y op valid ; If we want to reset the model (such as when we press the "clear" button): : reset ( model -- ) We're storing all values as floating-point numbers, but (for display purposes) we'll show integers when possible: : display ( n -- str ) Each of : set-x ( model -- model ) Pushing the "=" button triggers the calculation: : (solve) ( model -- ) We support negating the number: : negate ( model -- ) And pushing the "." button (to add a decimal), or a number (to add a digit): : decimal ( model -- ) That pretty much rounds out the basic features of the model. The GUIFor convenience, I store the calculator model in a global symbol: SYMBOL: calc I can use that to create buttons for each type (using short names and unicode characters to make the code a bit prettier): : [C] ( -- button ) We will create a label that is updated when the model changes. : <display> ( -- label ) And, finally, creating the GUI (using vertical and horizontal track layouts): : <col> ( quot -- track ) Then, running the calculator application: ( scratchpad ) "calc-ui" run The code for this is on my Github. John Benediktsson: Building "cat"
One neat feature of Factor is the ability to create and deploy programs as compiled binaries -- both CLI (command-line) or UI (graphical) applications. I thought it might be fun to build the cat command-line program in Factor, and show how it can be deployed as a binary. From the man pages: The cat utility reads files sequentially, writing them to the standard output. The file operands are processed in command-line order. If file is a single dash ('-') or absent, cat reads from the standard input. We'll start by creating the ( scratchpad ) USE: tools.scaffold Begin the implementation by listing some imports and a namespace: USING: command-line kernel io io.encodings.binary io.files Printing each line from a stream is easy using the each-line word (flushing after each write to match the behavior of : cat-lines ( -- ) I chose to treat files (which might be text or binary) as binary, reading and writing 1024 bytes at a time. We check that the file exists, printing an error if not found: : cat-stream ( -- ) Given a list of files, with a special case for "-" (to read from standard input), we can : cat-files ( paths -- ) Finally, we need an entry point that checks if command-line arguments have been provided: : run-cat ( -- ) Using the deploy-tool: ( scratchpad ) "cat" deploy-tool Click "Save" to persist the deploy settings into a Deploying cat... And your binary should be in the same directory as your Factor installation (in a $ ls -hl cat.app/Contents/MacOS/cat The code for this is on my Github. |
Blogroll
planet-factor is an Atom/RSS aggregator that collects the contents of Factor-related blogs. It is inspired by Planet Lisp. |