Re: [Haskell] Image manipulation

2007-10-31 Thread Bjorn Lisper
Jerzy Karczmarczuk:
Dan Piponi adds to a short exchange: 

 jerzy.karczmarczuk:
 [iso-8859-1] Bj�rn Wikstr�m writes: 

  Hi! I have lots and lots of images (jpegs) that I would like to manipulate
  and shrink (in size). They are around 5 Mb big, so I thought this would
  be a good Haskell project since it's a lazy evaluating language.
 ...
 I must say that I don't see much use of laziness here.

 Laziness plays a big role in real world image processing. Typically,
 in applications like Apple's Shake, you build a dataflow
 representation of the image processing operations you wish to perform,
 and the final result is computed lazily so as to reduce the amount of
 computation. For example, if you blur an image, and then zoom in on
 the top left corner, then only the top left corner will be loaded up
 from the original image (assuming your image file format supports
 tiled access). You still work on tiles or scan-lines, rather than
 individual pixels, so the laziness has a 'coarse' granularity. 
 
 But I'm not sure if this is what the original poster was talking about.

I am neither...
Still, Dan, I think that there is quite a difference between incremental
processing of signals, and images, etc., and the *lazy evaluation* of
them. Of course, a stream is consumed as economically as it can, but
not less. If you filter an image (shrinking, so some low-pass MUST be
done), a pixel must be loaded with its neighbourhood, which means *some*
scan lines.
With a JPEG this means that a 8x8 block should be loaded also with its
vicinity. But would you suggest that individual pixel processors should
be lazy? It would be useless, and probably resulting in some penalties. 

So, the laziness of Haskell for me here is less than useful.
Nw, the lazy *generation* of streams is another story...
Generating music (low level, i.e. sound patterns) through lazy algorithms
is quite interesting. 

Wait, Jerzy. Haskell implementations do not have to be lazy as long as they
preserve the more abstract semantics. Optimistic evaluation has been
considered, for instance by Robert Ennals. My former PhD student Karl-Filip
Faxen studied optimizations based on cheap eagerness, simple cases where
it is easy to see that it pays off to generate code that is not totally
lazy. One case is if we know that a computation always terminates and takes
little time, then it can typically pay off to perform that computation
optimistically even in cases where a lazy evaluation strategy might have
avoided it.

For image processing, if we know that individual pixel computations
terminate then the compiler is free to carry (a finite number of) them out
in any order, even if some of them turn out not to be needed. So tiled
computation schemes are certainly possible, even if parts of some tiles fall
outside the final image.

Another thing is that current Haskell compiler don't work like this. But
Haskell itself does not prevent it.

A number of years back I worked on something called the data field model,
a kind of generalized array construct intended for high-level data parallel
programming. We also defined and implemented a dialect of Haskell called
Data Field Haskell which extended Haskell with one particular instance of
data fields. Data Field Haskell had a function to force the evaluation of
all elements in a data field at once. The purpose of this function was
exactly to make possible computations like the above, where it pays off to
evaluate data fields, or parts of them, in a non-lazy fashion. I am quite
confident that it would be easy to express the image processing mentioned
above in Data Field Haskell.

Alas, we were never able to do anything else than a proof-of-concept
implementation. If anyone is interested to see what we did, there was a
paper in the Haskell Workshop 2000.

Björn Lisper
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell-cafe] Is Excel a FP language?

2007-05-05 Thread Bjorn Lisper
  I just had a thought... Why doesn't somebody implement a spreadsheet 
 where
 Haskell is the formula language? 8-)

 http://sigfpe.blogspot.com/2006/11/from-l-theorem-to-spreadsheet.html
 may interest.

...ok, and now my head hurts...

(Haskell seems to do that lots. I'm not sure why.)

Well, check out http://www.mrtc.mdh.se/index.php?choice=projectsid=0041

This might ease the headache a little. It is basically the result of a MSc
thesis project that a student did for me a couple of years ago.

Björn Lisper

PS. Also check out http://www.cs.kent.ac.uk/projects/vital/
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Generalizing three programs

2007-02-06 Thread Bjorn Lisper
Queuing theory is a very large and mature area of
research, with many important applications in
industry. It is not a coincidence that a certain
telephone company named a functional programming
language after Erlang, the founder of queuing
theory.

Erlang actually stands for Ericsson Language. I think the alternative
interpretation is intentional, though.

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Is Excel the most used -- and fucntional -- programming lanuage on Earth?

2007-01-31 Thread Bjorn Lisper
Dan Piponi:
On 1/30/07, Lennart Augustsson [EMAIL PROTECTED] wrote:
 Excel is what I like to call a 0:th order functional language,
 i.e., you can't even define functions, just values.  :)

Every cell with an expression in Excel is a function. The problem is
that the domains and codomains of these functions don't usually
contain functions. Maybe that makes it a first order functional
language.

Or maybe not. Yes, every cell in isolation contains an expression possibly
with free variables, and so can be seen as a function in those
variables. But these variables are not unbound since they are defined
elsewhere in the spreadsheet. Thus, the sheet is rather a system of
equations defining values, not functions. I think 0:th order is a good term
:-)

But...suppose we had a spreadsheet a little like Haskell where each
cell has a static type, and the values can be Haskell functions. What
interesting things could we do with it that we couldn't do with Excel?

I had a MSc student doing something in this direction some years ago. He
made a Haskell interface which was intended to work like a spreadsheet. In
this interface, every declaration has a value window (if the entity declared
has a showable type) and a declaration window. A designated button triggers
a recompilation, and thus also a recalculation of all declared values -
this, I think, captures the essence of spreadsheets which is to be able to
make changes and quickly see the results. In order to support the kind of
array omputations often done in spreadsheets, an extended array module was
defined which declares a number of array functions and other conveniences.

See http://www.mrtc.mdh.se/index.php?choice=projectsid=0041.

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Simple matrix

2006-06-26 Thread Bjorn Lisper
Udo Stenzel:
Bjorn Lisper wrote:
 - your definition of fromInteger will behave strangely with the elementwise
   extended operations, like (+). 1 + [[1,2],[3,4]] will become
   [[2,2],[3,5]] rather than [[2,3],[4,5]]. Array languages supporting this
   kind of overloading invariably have the second form of semantics.

Don't call an array a matrix.  If is named matrix, it should have
matrix multiplication, addition, and they should obey the expected laws.  

But you still have the problem with the overloading of constants in your
proposal. If you write 17 + a, where a is a matrix, what do people in
general expect it to be?

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Simple matrix

2006-06-22 Thread Bjorn Lisper
I wrote:
Here is one way to do it. First, you have to interpret operations on
matrices as being elementwise applied. E.g, (*) is interpreted as zipWith
(zipWith (*)) rather than matrix multiply, and similar for (+) etc. You then
obtain a lazy semantics for the operations, where the extent of the
resulting matrix is the intersection of the extents of the argument
matrices. Second, you lift constants into infinite matrices containing the
constant, that is: fromInteger n = repeat (repeat n). Now your examples will
work as intended.

Ah, should of course be fromInteger n = repeat (repeat (fromInteger n)).

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Simple matrix

2006-06-22 Thread Bjorn Lisper
Udo Stenzel:
Bjorn Lisper wrote:
 Here is one way to do it. First, you have to interpret operations on
 matrices as being elementwise applied. E.g, (*) is interpreted as zipWith
 (zipWith (*)) rather than matrix multiply

What's this, the principle of greatest surprise at work?  Nonono, (*)
should be matrix multiplication, fromInteger x should be (x * I) and I
should be the identity matrix.  Now all we need is an infinitely large
I, and that gives:

instance Num a = Num [[a]] where
   (+) = zipWith (zipWith (+))
   (-) = zipWith (zipWith (-))
   negate = map (map negate)
   fromInteger x = fix (((x : repeat 0) :) . map (0:))
   m * n = [ [ sum $ zipWith (*) v w | w - transpose n ] | v - m ] 

There are pros and cons, of course. Using (*) for matrix multiply is
well-established in linear algebra. But:

- it breaks the symmetry. This particular operator is then overloaded in a
  different way than all the others, and

- your definition of fromInteger will behave strangely with the elementwise
  extended operations, like (+). 1 + [[1,2],[3,4]] will become
  [[2,2],[3,5]] rather than [[2,3],[4,5]]. Array languages supporting this
  kind of overloading invariably have the second form of semantics.

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Simple matrix

2006-06-21 Thread Bjorn Lisper
Here is one way to do it. First, you have to interpret operations on
matrices as being elementwise applied. E.g, (*) is interpreted as zipWith
(zipWith (*)) rather than matrix multiply, and similar for (+) etc. You then
obtain a lazy semantics for the operations, where the extent of the
resulting matrix is the intersection of the extents of the argument
matrices. Second, you lift constants into infinite matrices containing the
constant, that is: fromInteger n = repeat (repeat n). Now your examples will
work as intended.

Björn Lisper


Atila Romero:
Good point.

And there is another problem: one could expect
10 * [[1,2],[3,4]] to be equal to [[10,20],[30,40]]
and in this case 10 should be equal to [[10,0],[0,10]], instead of 
[[10,10],[10,10]] or [[10]].

I dont see how to fix this.
Could be better to forget about fromInteger...

Atila

Jared Updike wrote:
   fromInteger x = [[fromInteger x]]

 Wouldn't you want the expression

 [[1,0],[0,2]] + 10

 to yield

 [[11,10],[10,12]]

 instead of [[11]] ? I guess you would need some complicated machinery
 so this is one thing you have to ignore to keep your otherwise nifty
 instance nice and simple.

  Jared.


   
___ 
Novidade no Yahoo! Mail: receba alertas de novas mensagens no seu celular. 
Registre seu aparelho agora! 
http://br.mobile.yahoo.com/mailalertas/ 
 

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] The values of infinite lists

2006-05-10 Thread Bjorn Lisper
Are the values of infinite lists _|_ (bottom)?

No. _|_ represents total lack of information about the result, whereas in a
lazy language like Haskell an infinite list contains a lot of information:
you can observe arbitrary parts of such a list, access them, and compute
with them.

In section 1.3, the Haskell 98 report said as follows:

   Errors in Haskell are semantically equivalent to _|_. Technically,
   they are not distinguishable from nontermination, so the language
   includes no mechanism for detecting or acting upon errors.

The formulation in the Haskell report is sloppy to say the least, and
clearly misleading as witnessed by your mail. Nontermination is not the
precisely the same as _|_. Only certain kinds of nontermination can be
modeled by _|_ in a non-strict language.

I think one should consider reformulating the paragraph above in future
versions of the Haskell report.

Therefore, the value of the following infinity is _|_. Right?

   data Nat = Zero | Succ Nat

   infinity = Succ infinity

No. Consider the following function:

f Zero = 0
f (Succ _) = 17

We then have f infinity = f (Succ infinity) = 17, whereas f _|_ = _|_.
Thus, f distinguishes infinity and _|_, so they can not be the same.

What about infinite lists? For example, is the value of [1 ..] also _|_?

No, see above.

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] The values of infinite lists

2006-05-10 Thread Bjorn Lisper
Deokhwan Kim:
Bjorn Lisper wrote:

 precisely the same as _|_. Only certain kinds of nontermination can be
 modeled by _|_ in a non-strict language.

What kinds of nontermination are modeled by _|_ in Haskell?

Nonterminating computations that never return anything. For instance,

inf = inf

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell] Compilation of big, computed tables

2006-02-23 Thread Bjorn Lisper
Chris Kuklewicz: 
Stefan Karrmann wrote:
 Dear all,
 
 can ghc compile huge tables into efficient code if they are constant at
 compile time?
 
 Two examples may clearify the question:
 
 big1 :: UArray Int Char
 big1 = array (0,1000) $! map (\i - (i,toEnum i)) [0..1000]
 
 big2 = sum [0..1]::Int -- == 50005000 == n*(n+1)/2 where n = 1
 

GHC does not compute such values are compile time because
*) The computation may not finish in reasonable time (infinite/not halting)
*) The computation may generate a run-time error and crash the compilation
(divide by zero, pattern march failure, etc.)
*) Haskell is supposed to be lazy, things are computed when needed, not before

Does this mean that GHC does not evaluate constant subexpressions at
compile-time? Or does it evaluate only some subclass of surely terminating
and non-erroneous subexpressions at compile-time?

Constant subexpression evaluation is a common compiler optimization
technique, so I would be a bit surprised if GHC does not do it.

Björn LIsper
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell-cafe] Numerical methods in Haskell

2006-02-20 Thread Bjorn Lisper
I finally got some time to answer Simon's posting:

Simon P-J:
| Between google searching and looking through the activity
| report, I take it that no one has really developed serious
| libraries for matrix manipulations, diff eqs, etc.
| 
| Are there any practical reasons for this or is it just a
| matter of the haskell community being small and there not
| being many people interested in something so specialized?

The latter I think, but it's just the sort of thing that a functional
language should be good at.  Two other difficulties

(a) It's hard to compete with existing libraries.  The obvious thing is
not to compete; instead, just call them.  But somehow that doesn't seem
to be as motivating.  Perhaps some bindings exist though?

Hard to compete, yes. But on the other hand, the rewards can be high.
Numerical library code (especially matrix libraries) tends to be highly
optimized for the hardware architecture at hand. As a consequence a small
change in, say, the cache architecture, might require a thorough rewrite of
the library code to regain high utilisation of the hardware. This is since
languages like Fortran and C force you to code so close to the hardware. A
high-level language, with good optimizing compilation, would make also
library code more portable across hardware architectures. N.b. these
optimizations are non-trivial to say the least.

(b) A concern about efficiency, because numerical computation is
typically an area where people really care about how many instructions
you take.  It's a legitimate concern, but I don't think that it'll turn
out to be justified.  With unboxed arrays, and/or calling external
libraries for the inner loops -- and the potential for aggressive fusion
and/or parallelism, there is plenty of upward potential.  I also want to
work on nested data parallelism (a la NESL, and NEPAL) which fits right
in here.

The number of instructions is only one side of the coin. For
high-performance computing, memory issues are at least as important: both
the amount of memory used (e.g., will the computation fit into memory at
all), and how the memory hierarchy is utilized (caches, TLB:s, virtual
memory, ...). This is a really sweet spot of functional languages, and
laziness adds to it.

On the other hand, the increased abstraction of functional languages gives
an optimizing compiler larger freedom to reorder computations and choose
memory layouts of data structures like matrices. This is potentially very
useful, since optimizing for memory hierarchy utilization typically involves
both data layout and order of memory accesses. However, to achieve a good
result, the compiler must be able to predict a great deal of the computing
and the memory usage. For instance, dynamic memory handling of numeric data
structures will surely kill any serious attempt to predict the cache
behavior. To achieve good optimizing compilation, we need either very good
program analyses, or a library of recursion patterns or templates for
which the compiler knows how to allocate memory statically and order the
computations well, or possibly both.

Some encouraging examples: Sven-Bodo Scholz has achieved very good
performance for the restricted functional matrix language SAC, using
optimizations for cache. My former student Peter Drakenberg invented a
restricted functional matrix language, with analyses to infer matrix sizes
statically, and sharing analysis, to find opportunities to allocate memory
statically and update in-place. He also got some good experimental figures.
This leads me to believe that compilers in more general languages could do
something similar, by recognizing certain patterns or through advanced
program analyses.  However, both these languages are strict, and I am not
sure at all how to do this in a lazy language.

In any case, this is nontrivial compiler work and considerable research
efforts would be needed. Unfortunately, I don't see how to fund such
research, since the high-performance computing community largely seems to
have given up on functional languages since the demise of the data-flow
languages.

I'd love to see a little community of matrix manipulators spinning up.  

Yes. There might me a niche for high-level numerical coding, somehwere where
MATLAB is today. MATLAB isn't exactly blazingly fast, still very widespread.
On the other hand, MATLAB is already in that niche. The question to answer
is what advantages a functional language like Haskell could offer in this
niche. We need to come up with these answers, and then convince enough
people outside our own community.

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Numerical methods in Haskell

2006-02-20 Thread Bjorn Lisper
David Roundy:
On Mon, Feb 20, 2006 at 11:47:49AM +0100, Bjorn Lisper wrote:
 (a) It's hard to compete with existing libraries.  The obvious thing is
 not to compete; instead, just call them.  But somehow that doesn't seem
 to be as motivating.  Perhaps some bindings exist though?
 
 Hard to compete, yes. But on the other hand, the rewards can be high.
 Numerical library code (especially matrix libraries) tends to be highly
 optimized for the hardware architecture at hand. As a consequence a small
 change in, say, the cache architecture, might require a thorough rewrite of
 the library code to regain high utilisation of the hardware. This is since
 languages like Fortran and C force you to code so close to the hardware. A
 high-level language, with good optimizing compilation, would make also
 library code more portable across hardware architectures. N.b. these
 optimizations are non-trivial to say the least.

The only particularly relevant numerical libraries today (atlas and fftw)
already do far better optimization than any compiler is going to acheive,
and in the case of fftw can respond to changes in memory configuration at
runtime.  In both cases they're written in ANSI C (although the C code for
fftw is written by an OCaml program... or at least some dialect of ML).  In
order to take advantage of cache behavior properly, it's necesary to allow
adjustments in the actual algorithm used, which isn't something that a
clever compiler is likely to accomplish.

That's a valid point. You may want to change, e.g., the size of blocks in a
block-oriented matrix algorithm to match the cache. This will, in general,
require the use of algebraic laws like associativity and commutativity,
which are not valid for floating-point arithmetics and thus can change the
numerical behaviour, so a compiler shouldn't fiddle around with them unless
under strict control of the programmer. Interestingly, the language invented
by my aforementioned former PhD student contains a nondeterministic matrix
decomposition primitive, which allows the partitioning of a matrix into a
fixed number of blocks, but where block sizes can vary. This is exactly to
let the programmer give an optimizing compiler this degree of freedom when
deemed safe. Alas, he never got around to any serious experiments with this
feature.

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] First steps in Haskell

2005-12-19 Thread Bjorn Lisper
Simon P-J:
Daniel is right, by definition.  He is a new user.  He had difficulty.
That much is incontrovertible.

While he may seem unusual, perhaps he is only unusual in that he's told
us about his experience rather than trying Perl instead.  For which,
much thanks, Daniel!

Actually, I have sometimes wished that the various interactive Haskell
interfaces had the possibility to enter also declarations interactively, as
Daniel originally asked for. Lisp interpreters often support this to some
extent, so it is not out of line for a newcomer to expect it also for
Haskell. I often use hugs as a calculator, and sometimes it would be very
convenient to be able to make one or a few short declarations while making
some interactive calculations. It can be quite tedious to create and edit a
source code file on the side and then load it, when all you want is a short
declaration or two.  Would it be that complex to have an interactive
interface enter a declarations mode when it sees a declaration coming, and
then let it create, compile and load a temporary module when the declaration
is finished?

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] First steps in Haskell

2005-12-19 Thread Bjorn Lisper
Simon:
Me:
| Actually, I have sometimes wished that the various interactive Haskell
| interfaces had the possibility to enter also declarations interactively

GHCi does.

Ah, I see! Does it open a let-environment with a local definition?

ghci  let f x = hello
ghci f True
True

Hmm, an interesting semantics for f given the declaration :-)

But there's no editor -- it's strictly a one-line definition

Which is useful enough in many situations. I'll try it out...

Björn
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell] why don't we have const Ptrs?

2005-11-02 Thread Bjorn Lisper
Hi,

Annotated type systems have been around for some time in static program
analysis. I think this is what you want. For instance, you can design such a
system to record possible side effects from a function call, as annotations
on the type of the function.

See the book Principles of Program Analysis,
http://www2.imm.dtu.dk/~riis/PPA/ppa.html.

Björn Lisper


David Roundy:
Hello all,

I was thinking this morning as I lay on the floor (where I sleep) about
static typechecking, and how much more wonderful Haskell is than any other
language, when it occurred to me that when working with pointers, Haskell
actually has *less* static typechecking than C or C++.  It was a very
disturbing thought, so much so that I was almost compelled to arise early
to place this question before this learned audience.

Why is it that in C++ I can write

void strcpy(char *dest, const char *src);

but in Haskell I must import this function as

 foreign import ccall unsafe static string.h strcpy
  strcpy :: Ptr CChar - Ptr CChar - IO ()

and lose that wonderful information that the function doesn't modify the
contents of its second argument?

One could pretty easily create a ConstPtr type which one could peek into,
but not poke to, but then you'd have to explicitely convert a Ptr into a
ConstPtr when passing it as an argument.  That feels a bit silly.

One could get around this by introducing a class to get around this

 class ReadablePtr p where
peek :: p a - IO a
peekOff ...

and then make both Ptr and ConstPtr instances of this class, but this still
seems like a very hackish solution.

Moreover, I'd like to be able to have const objects quite apart from Ptrs,
such as a const Handle, which I can read from, but cannot write to, or a
const IORef--and we wouldn't want to leave out const ForeignPtrs.  Of
course, even reading affects a Handle's internal state, so one would need
to be explicit about what const indicates.  But it seems to me that in
the IO world there are a whole slew of things that refer to other things
which could all be grouped together.

And a const attribute ought to be derived, so that if I create a data
type

 data FooPtr = FooPtr String (Ptr Foo)

one should ideally be able to automatically understand that a const FooPtr
holds a const (Ptr Foo).

One could go further, at least when dealing with Ptrs, and create a way of
handling restricted pointers--which we could interpret as a const pointer
to an object that cannot be changed by anyone else either.  One could
safely create restricted pointers with a function of the type

 mallocRestrictedPtr :: (Ptr a - IO ()) - RestrictedPtr a

which would allow one to ensure at the typechecking level that
RestrictedPtrs point to memory that cannot be modified.  There's still some
unstafety involved, in that you could read out of bounds, but you would
know that apart from that possibility the contents of a RestrictedPtr truly
will never change.

So my question is, how would one implement such an annotation extension?
I'd like to be able to pass a (Ptr a) as a (Const (Ptr a)) without an
explicit typecast, since the Const really isn't changing the type of the
pointer, it's just marking it as one that can't be modified.  A function
that accepts a (Const (Ptr a)) should also accept a (Restricted (Ptr
a))--but Restricted pointers are really just pudding, as they only differ
from Const pointers in what optimizations are allowed.  On the other hand,
it's not just compiler optimizations that they would allow, but also
user-code optimizations, which could be much more useful.  They also have
the advantage of making certain unsafe functions safe.

The hard part seems to be the lack of a conversion.  One could quite easily
implement a

 data Const a = Const a  -- this constructor is *not exported*
 toConst :: x - Const x
 unsafeAccessConst :: Const x - x

 peek :: Const (Ptr a) - IO a
 peekOff ...

etc, and everything would work fine, except that you'd always need to
explicitely convert from Ptr to Const Ptr.  Perhaps one could make Const be
a class as well as a data type:

 class (Const a) x where
 toConst :: x - Const a
 instance (Const x) x where
 toConst = Const
 instance (Const x) (Const x) where
 toConst = id

and then one could write code like

 peek :: Const (cp a) = cp a - IO a

which would move the typecasting burden out of the calling function and
into the function that accepts a const argument.  Perhaps this would be
sufficient, as many data types have only a limited number of primitive
const functions, and all the other const functions wouldn't actually
need to call toConst.

What this doesn't allow is deriving of constness, so that a Const
ForeignPtr would automatically hold a Const Ptr.

This whole class+data type scheme seems like it might be useful, but is
pretty ugly.  Is there a better way this could be done?

Might one be able to extend the language so that one could add attribute
such as Const to data type without changing the the 

Re: [Haskell] getArgs, maxBound, float division: pure functions?

2005-10-12 Thread Bjorn Lisper
John Meacham:
On Wed, Oct 12, 2005 at 12:05:39AM -0400, David Menendez wrote:
 In principle, you could use seperate types to distinguish floats with
 different rounding modes, but I imagine this would be difficult or
 annoying to implement.

I think it would make more sense to have different operations for each
rounding mode. That matches the reality of the situation more and you
don't want to have to do tons of type conversions when you need to
operate on something with different rounding modes. of course, then you
could declare several 'newtypes' whose default Num instances were of
specific rounding modes too.

A student of mine designed a special-purpose, pure functional language for
block-oriented matrix computations for his PhD. He was very careful to give
the language a good design also regarding floating-point computations. His
design choice, as regards rounding, was to allow the compiler to choose
rounding mode by default (thus allowing more freedom for optimization),
while providing a set of special arithmetic operators, with specified
rounding modes, to use when more explicit control is needed.

He also proposed a special construct letctrl ... in e, where the ... are
a list of directives telling how to interpret and evaluate the expression
e. One possible directive is RoundingMode = ... to set the rounding mode
locally in e. Other directives control, for instance, whether optimizations
like x*0.0 - 0.0 are allowed in e, whether to force strict evaluation of
all subexpressions (so optimizations cannot affect exceptions), to set
allowed miminum accuracy, etc.

The language has exception handling (including a handle construct to catch
exceptions), which also takes care of floating-point exceptions.

If Haskell ever gets redesigned to give better support for numerical
computations, then I think this work is worth a look. Alas, it is not
well-published, but the PhD thesis should be possible to order from the
Dept. of Information Technology and Microelectronics at KTH. I also have a
few copies to give away of someone is interested.

Here's the reference:

@PHDTHESIS{npd-phd,
AUTHOR = {N. Peter Drakenberg},
TITLE = {Computational Structure and Language Design},
SCHOOL = {Dept.\ of Information Technology and Microelectronics, KTH},
ADDRESS = {Stockholm},
YEAR = {2004},
MONTH = sep,
NOTE = {TRITA-IMIT-LECS AVH 04:02}
}

Björn Lisper
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] Mixing monadic and non-monadic functions

2005-09-14 Thread Bjorn Lisper
Ben Rudiak-Gould:
Frederik Eaton wrote:
 I want the type system to be able to do automatic lifting of monads,
 i.e., since [] is a monad, I should be able to write the following:
 
 [1,2]+[3,4]
 
 and have it interpreted as do {a-[1,2]; b-[3,4]; return (a+b)}.

The main problem is ambiguity: [[1]]++[[2]] could be [[1],[2]] or [[1,2]], 
for example. If your proposal resolves this ambiguity in favor of one result 
or the other, then I'm opposed to it -- it's far too easy in practice to 
write code like this, expecting it to be lifted, and failing to notice that 
it also has an interpretation without lifting (or the other way around). 
This is the Perl FWIM disease[1] -- not that I dislike Perl, but people are 
quite rightly leery about bringing this sort of thing into Haskell.

However, there is a way to resolve the ambiguity that can be claimed to be
the most natural one, and that is to always choose the least possible
lifting. In the example above, this would mean to interpret [[1]]++[[2]]
precisely as [[1]]++[[2]] (lift 0 levels) rather than [[1]++[2]] (lift 1
level). This is akin to choosing the most general type in the pure
Hindley-Milner type system, and it has the advantage that expressions that
are typable in the original type system, without lifting, retain their
semantics in the type system with lifting added.

Lifting (mainly of arithmetic operators) has been around for a long time in
array- and data parallel languages such as Fortran 90 and *lisp.  The kind
of ambiguity mentioned here was first observed for nested data-parallel
languages like NESL, which use nested sequences as parallel data structures.

Now, whether to include this kind of lifting in Haskell or not is an
entirely different story. One complication would be to handle possible
clashes between lifting and overloading through the class system. IMHO, I
think it would be interesting to be able to define application-specific
Haskell dialects, which can have this kind of lifting for a restricted set
of types and/or functions, whereas having it as a general feature of the
language would be quite problematic.

Björn Lisper
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell-cafe] some algorithm help with jhc

2005-09-14 Thread Bjorn Lisper
John Meacham
I have started working on jhc more recently and have come across some
places where I think my algorithms could be improved but was not sure
exactly where to start so thought I would ask the list since perhaps
someone here has some insight.

After a long time of trying various methods of speeding up the fixpoint
iteration of my points-to analysis (the current main bottleneck) I
decided to step back and look at the basic problem again. It turns out I
can express the problem as one of constraint satisfaction resulting in
much smaller code (600 lines vs 2000) and 10fold speedups with my
unoptimized first draft solver.

It is much faster but still not as fast as I'd like. I don't know a lot
about constraint problems, but my intuiton says this particular problem
is of a type that should be particularly easy to solve but am uncertain
where to start in my searching to find a fast algorithm. My constraints
come in two types of rules.



Hi, check out the book Principles of Program Analysis by Nielson, Nielson,
Hankin (http://www2.imm.dtu.dk/~riis/PPA/ppa.html). It has quite some on
constraint solving for program analysis. There are algorithms in that book
for set constraint problems that look quite similar to your problem.

Björn Lisper
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Python?

2005-05-18 Thread Bjorn Lisper
Hi all,

Finally I found some time to reply to this posting. A couple of years ago we
did something called Data Field Haskell, which is Haskell extended with a
generalized form of arrays called data fields. Much of the purpose was to
investigate convenient and general syntax for array constructions.

Data fields are semantically pairs of functions and bounds, where bounds
represent sets of indices. Bounds can be pairs of integers representing
intervals, but also general finite sets. Certain bounds represent infinite
sets, which makes sense in a lazy language.

Besides direct notation for creating data fields, Data Field Haskell has a
construct forall x - t which defines a data field in a similar way to how
lambda-abstraction \x - t defines a function. In addition, the data field
defined by a forall-construct has a bound which is derived from the body of
the expression. The semantics is derived from the view that data fields (and
arrays) are partial functions whose domains do not exceed the index sets
defined by their bounds.

The forall-construct is intended to provide a simple and powerful syntax for
operations on data fields. Let's see below how it matches Jerzy's desired
features of an array language:

Jerzy:
Tim Rowe writes: 

 On 5/11/05, Jerzy Karczmarczuk [EMAIL PROTECTED] wrote: 
 
 Give me one single language where [3-d arrays are] natural and immediate.
 
 I don't know how Matlab does it, but I find the C++ standard library
 vectorvectorvectorfloat  
 entirely intuitive (apart, perhaps, for the need for those two spaces)!

Let's precise what I consider to be important in order to call it natural
and immediate. And, in general, useful. 

1. The definition of a concrete object, not just its type, but, say,
  the initialization with constants. Or/and, global initialization with
  zeros. 

Since Data Field Haskell is declarative, initialization and definition is
the same. Constant data fields are easy to define, viz.

forall x - 1, a data field filled with ones
forall (i,j) - if i == j then 1 else 0, a unit matrix
forall (i,j) - i + j, a Vandermonde matrix

All these data fields are infinite, and the first is also
dimension-polymorphic (through the usual polymorphism in Haskell). Data
Field Haskell has an operator for explicit restriction of data fields, and
a data field may also be implicitly restricted from the context where it
occurs.

2. Easy synthesis of multi-dim matrices out of planes, of submatrices
  of lesser dimensions;

Not directly expressible, but it is possible to define a binary operator to,
say, concatenate a 2-D matrix on some given side of a 3-D matrix.

  it can be an 'overlay', like, say, making  a colour image out of three
  R/G/B planes, or making a 3D surfaces, or aking tensors through
  external products. 

Say, an outer product of two vectors a, b: forall (i,j) - a!i * b!j

3. Easy indexing, and not only A[i][j][k], etc., but slicing, the extraction
  of sub-dimensional matrices, e.g., one column vector out of a 2D matrix
  in Matlab:  A(3,:). Also, extracting parts (e.g. sub-images). 

A(3,:) is expressed as forall j - a!(3,j). Or, selecting a plane out of a
3D-matrix: forall (i,j) - a!(i,3,j). Or, selecting the main diagonal of a
matrix: forall i - a!(i,i).

Extracting parts is done with the explicit restriction operator \:

a \ (3,3):(10,20), selecting submatrix of a
a \ predicate (\(i,j) - a!(i,j)  0), selecting the subfield of a where
it is strictly greater than zero (this is a sparse data field)

  Also, in mathematical context, intelligent indexing, e.g. treating
  a matrix as implicitly anti-symmetric. Here the CAS systems as Maple
  or Mathematica provide the adequate tools. C++ of course doesn't,
  unless you overload [] yourself. 

All representation issues are hidden in Data Field Haskell, so there is no
direct counterpart. However, it is of course possible to define a data
structure of your own and then wrap it up as a data field by providing a
bound and a function from indices to values.

4. Readable iterators, perhaps something more compact than insipid do-loops. 

There are folds for data fields. These can be used for reduction operations
over data fields. In a sense, forall-abstraction is also a kind of iterator
(although parallel since it does not impose any order of evaluation
between the elements of the data field).

5. If those matrices are used as mathematical objects: tensors, etc.,
  I want to have some simple notation for inner multiplications/
  contractions, etc. This is not just the syntax problem, but the
  existence of good libraries as well... 

You can use folds inside a matrix to, e.g., sum all rows in a matrix. Here
is an example of a function that does one iteration in Jacobi's method for
iteratively solving the linear equation system ax = b:

jacobi_iter a b x = 
  forall i - ((b!i) - dfSum ((forall j - a!(i,j)*(x!j))
  \ predicate (\j - j /= i)))/a!(i,i)

6. Reshaping of those arrays. I thought that Matlab 

Re: [Haskell] Typing in haskell and mathematics

2005-01-31 Thread Bjorn Lisper
Jacques Carette:

Yes, I am aware that this untypeable in Haskell, because polymorphism is 
straight-jacketed by structural rules.  But 
in mathematics, it is convenient and extremely common to:
1) look at the type a - b as being a *subtype* of b (though I have never seen 
it phrased that way in print)
2) to consider every 'natural transformation' available and use it (tuples of 
functions - obvious function on 
tuples)

Seeing a - b as a subtype of b allows one to take any function f : b - b - 
c (like + ) and 'lift' it to 
ff : (a - b) - (a - b) - (a - c)
via the obvious definition
ff g h = \x - f (g x) (h x)
This is done routinely in mathematics.  The meaning of the expression (sin + 
cos) should be immediately obvious.  Such 
'lifting' is used even in first-year calculus courses, but not in Haskell.

I would like to remark that some array languages are doing this kind of
overloading (e.g., A + B means elementwise addition of A and B, when A and B
are arrays). If arrays are seen as functions from finite index domains, then
this is just a special case of your math overloading. Examples of such
languages are Fortran 90, HPF, and (the specified, but never implemented)
functional language Sisal 2.0.

All these languages are first-order, explicitly typed, and provide this
overloading only for a predefined set of builtin primitives. However, I
think this can be extended to functions in general, in higher-order
languages with Hindley-Milner (HM) type inference. I had a PhD student
working on this a couple of years ago. Alas, he dropped out before getting
his PhD, but we got as far as defining an extended inference system with
some nice properties, and I do believe that a reasonable inference algorithm
can be found. Rather than using subtyping, this system uses
transformational judgements of the form A |- e - e':t, where e':t is
inferrable in the original HM type system. Thus, this system resolves the
overloading during type inference. For instance, with a proper assumption
set A, we would have A |- sin + cos - (\x - sin x + cos x).

Now, whether you would want to have this overloading unleashed in a higher
order language is another matter. I think it can be useful in some contexts,
but probably restricted in some way. Also, in Haskell there would be
potential conflicts with the overloading provided by the class system (Num
being a prime example).

Björn Lisper
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] Implicit parallel functional programming

2005-01-20 Thread Bjorn Lisper
I'd like to add my two cents worth in this debate...

I think the original poster considered the standard multicore processors
soon to come, and which can be expected to eventually overtake the processor
market. The answer relies a lot on what shape these processors will have:

A guess is that the first generation will support a shared memory model much
like SMP:s of today (shared main memory with on-chip cache(s), or some other
kind of local memory (-ies)). Here, I think implicit parallelism in
functional languages can be a win in some situations. This is provided
spawning of parallel threads is cheap and local memory can be efficiently
utilized (so control of granularity is no problem, and dynamic parallelism
is cheap). Cheap threads is likely to be true, efficient use of local memory
is a question mark.

However, some obstacles:

Laziness, if used strictly (sorry) gets in the way for parallelism. Waiting
to spawn a parallel thread until we surely know it's result is needed, if it
is anyway needed 99% of the time, is not an optimal strategy. I think
optimistic evaluation strategies a la Robert Ennals can be helpful here. But
designing them to work for new parallel architectures may be non-trivial.
I think there can be material for PhD theses here.

If the program implements an inherently sequential algorithm then
parallelism won't help anyway. The use of lists often leads to sequential
algorithms: a prime example is list folds, which all translate the linear
spine structure of a list into a sequential dependence graph of the
resulting computation. Folds over tree-like structures are better in this
regard.  The FP community will have a problem with legacy code using
inherently sequential computations over lists.  There has been quite some
research on how to parallellize list computations (skeletons, program
transformations using BMF,...). However, they typically rely on knowing the
length of the list. In a lazy language it is common that lists are evaluated
dynamically, and then it's hard to use these techniques efficiently. Again,
speculation may help.

In the long run, as hardware dimensions shrink even more and communication
becomes relatively even more expensive, it is likely that we will have some
kind of on-chip massively parallel, mesh-connected distributed memory
machine. This will most likely favor static parallelization techniques,
which means only for certain classes of functional programs an automatic
approach with implicit parallelism will work well.

Björn Lisper
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] Implicit parallel functional programming

2005-01-20 Thread Bjorn Lisper
Keean Schupke:
A guess is that the first generation will support a shared memory model much
like SMP:s of today (shared main memory with on-chip cache(s), or some other
kind of local memory (-ies)). Here, I think implicit parallelism in
functional languages can be a win in some situations. This is provided
spawning of parallel threads is cheap and local memory can be efficiently
utilized (so control of granularity is no problem, and dynamic parallelism
is cheap). Cheap threads is likely to be true, efficient use of local memory
is a question mark.
  

You only need one thread per CPU-core, so the spawning threads cost is
a red-herring. Just like GHC schedules haskell-threads from a single
OS-thread.

Then you need some mechanism to assign jobs to threads, which also carries
an overhead. How large that overhead is depends to some degree on the
architectural characteristics of the machine (some shared workpool is
necessary, which means the cost of common memory accesses comes into play).

However, some obstacles:

Laziness, if used strictly (sorry) gets in the way for parallelism. Waiting
to spawn a parallel thread until we surely know it's result is needed, if it
is anyway needed 99% of the time, is not an optimal strategy. I think
optimistic evaluation strategies a la Robert Ennals can be helpful here. But
designing them to work for new parallel architectures may be non-trivial.
I think there can be material for PhD theses here.
  

I suggest speculative execution, as used by current CPUs to take advantage
of multiple microcode execution units. If the a CPU has spare capacity, 
functions
whose results might be needed are run. As soon as you know a result is not
needed the thread can be killed, and the time used for something else. A lot
of work has already been done on hardware superscalar-architectures, and
a lot of this should be applicable to the software case.

Yes, precisely what I refer to above (the work of Robert Ennals). But you
need to choose well what to speculate on. This can be nontrivial.

If the program implements an inherently sequential algorithm then
parallelism won't help anyway. The use of lists often leads to sequential
algorithms: a prime example is list folds, which all translate the linear
spine structure of a list into a sequential dependence graph of the
resulting computation. Folds over tree-like structures are better in this
regard.  The FP community will have a problem with legacy code using
inherently sequential computations over lists.  There has been quite some
research on how to parallellize list computations (skeletons, program
transformations using BMF,...). However, they typically rely on knowing the
length of the list. In a lazy language it is common that lists are evaluated
dynamically, and then it's hard to use these techniques efficiently. Again,
speculation may help.
  

But in the worst case its just a sequential computation, so any gain from
parallelism is still a gain...

It depends on what you compare with. Multicore CPU:s will probably have
cores that are simpler than current processor cores, which means you will
want to have some parallelism. Cf. a superscalar processor, which really in
a sense is a parallel machine but where you add some complex control
hardware to make it emulate a sequential machine. Running a multicore CPU on
a single core at a time corresponds to running a superscalar machine where
you use none of the superscalarity. Or, running a pipelined CPU (another
form of parallelism) without utilizing any of the pipelining. This is not
what you want.

 also the dependant part of a loop is often
separate from an independant part... For example consider a list of 
strings, and
taking the length of each string. The next iteration does not have to wait
for the length to be computed, it can start the next length computation as
soon as the pointer for the next list cell has been dereferenced.

Sure you can get some overlap, which can be quite big if the task to perform
per list element is big, but a dependence graph with O(n) nodes will still
have O(n) depth = at maximum constant speedup.

In the long run, as hardware dimensions shrink even more and communication
becomes relatively even more expensive, it is likely that we will have some
kind of on-chip massively parallel, mesh-connected distributed memory
machine. This will most likely favor static parallelization techniques,
which means only for certain classes of functional programs an automatic
approach with implicit parallelism will work well.
  

The next generation of CPUs seem to be using a shared cache architecture,
so overheads are extreemely small...

I'm not talking about the next generation but further ahead. Maybe these
architectures will have some kind of on-chip shared cache, who knows, but
that memory will then still be much more expensive to access than the memory
in a local core. So you will want to use the core-local memory all the time,
and only communicate between processor cores 

Re: [Haskell-cafe] Integrating Haskell into a J2EE environment

2004-10-07 Thread Bjorn Lisper
I'm surprised that nobody has yet mentioned the ICFP2000 paper Composing
Contracts: An Adventure in Financial Engineering by SPJ, Jean-Marc Eber,
and Julian Seward. It seems to me that it could provide quite relevant
reading.

Björn Lisper


Paul Hudak:
I wouldn't write off Haskell so quickly.  All of what Shoeb describes 
concerning DSL issues might be much more easily solved in Haskell, and 
will certainly be more flexible than a hard-wired approach.  The J2EE 
interface might be ugly, but if the functionality needed is not too 
great it might not be too bad.  Generally speaking, these kinds of apps 
-- in this case a DSL for high-level business rules -- sounds like 
just the sort of thing that Haskell is good for.

   -Paul


Doug Kirk wrote:
 You're going to spend alot of time marshalling between Java and Haskell 
 values, and you'll either have to do it via JNI or by using pipes [as in 
 System.exec(haskellprogram param param param)], both of which are ugly 
 for a Java app.
 
 Have you looked at Jython and JRuby? Jython is an implementation of a 
 Python interpreter in 100% Java, and JRuby implements a Ruby interpreter 
 in 100% Java.
 
 Those might get the job done faster than having to delve into the native 
 layer. (Not to mention learning how to use Haskell in order to implement 
 what you want--not a trivial task in itself!)
 
 Take care,
 --doug
 
 
 On Oct 5, 2004, at 4:33 PM, Bhinderwala, Shoeb wrote:
 
 Hi All,
 
 I am new to Haskell and this mailing list.
 
 We have a system that uses a custom high-level language to express
 high-level business rules. Expressions in the high-level language get
 compiled to Java bytecode. We express the grammar using BNF notation as
 required by the javacc parser tool. This is then converted to an AST
 using jjtree and from there we build the final Java code. Our language
 could be considered a domain-specific language (DSL) and is used by our
 business users to express very high-level business logic. The language
 currently is very limited - we support boolean logic, function
 invocations and if-then statements. We want to convert it into a more
 powerful scripting language so that even lower level business logic can
 be expressed in it.
 
 I came across a few papers that talk about writing a DSL with Haskell as
 the underlying support language. How is this done. Is it possible to
 create a sort of domain specific business scripting language easily. How
 does that then compile to Haskell code. And how can the Haskell code be
 invoked from Java.
 
 Essentially, I am thinking if I could use a Haskell like DSL language to
 express our business rule logic and then be able to integrate into and
 invoke the logic from a J2EE app server environment. Has anybody done
 anything like this with Haskell.
 
 -- Shoeb
 ___
 Haskell-Cafe mailing list
 [EMAIL PROTECTED]
 http://www.haskell.org/mailman/listinfo/haskell-cafe
 
 
 
 This communication is confidential and may be legally privileged.  If 
 you are not the intended recipient, (i) please do not read or disclose 
 to others, (ii) please notify the sender by reply mail, and (iii) please 
 delete this communication from your system.  Failure to follow this 
 process may be unlawful.  Thank you for your cooperation.
 ___
 Haskell-Cafe mailing list
 [EMAIL PROTECTED]
 http://www.haskell.org/mailman/listinfo/haskell-cafe
 


-- 
Professor Paul Hudak
Chair, Dept of Computer Science   Office: (203) 432-1235
Yale University   FAX:(203) 432-0593
P.O. Box 208285   email:  [EMAIL PROTECTED]
New Haven, CT 06520-8285  WWW:www.cs.yale.edu/~hudak

___
Haskell-Cafe mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell-cafe
___
Haskell-Cafe mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell] 2-D Plots, graphical representation of massive data

2004-08-27 Thread Bjorn Lisper
Jerzy Karczmarczuk:
Jacques Carette wrote:

 John Meacham [EMAIL PROTECTED] wrote:

 What would be cooler (IMHO) would be brining all of matlabs
 functionality into haskell via haskell libraries so one may use 'ghci'
 sort of as one uses matlab, but with the advantages haskell brings.


 One could create Haskell libraries that are matlab-like, but most of 
 the advantages of haskell (ie stong typing) are not realizable in 
 Haskell.  To express even the most basic of matrix datatypes and 
 operations requires dependent types.

I did not understand what is not realizable where...

I wonder what for do you need those dependent types.
Matlab is quite orthodox, its main flexibility comes from the dynamical 
typing, resolution
of the dimensions at the run-time, etc. The Numerical Python gives a 
good share of Matlab
functionalities.
Now, overloading arith operations for some bulk data (lists, lists of 
lists, arrays, etc.) casting
them to some matrix general types, should not be impossible without 
dependent types.
Hm. I will not bet my head, but, please, *provide an example* of such 
situation.

I think Jacques possibly means the ability to do static checking of matrix
and vector extents, to make sure that you don't try to perform operations
like matrix-vector multiply on operands whose extents do not match. If you
want to have this ability on your language, then you will have to restrict
the way you are allowed to construct array bounds so the equations that
arise can be solved. Possibly a dependent type system can be helpful for
this.

Björn Lisper
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] Eliminating Array Bound Checking through Non-dependent types

2004-08-06 Thread Bjorn Lisper
( A really interesting post on static elimination of array bounds checking
by Oleg...)

Some questions and suggestions:

What is the relation to the sized types by Lars Pareto and John Hughes?

What is the relation to classical range analyses for (e.g.) array index
expressions, which have been known for a long time for imperative languages?

A program analysis like range analysis is not exact, of course: it must make
safe approximations sometimes and will sometimes say that an array index
might be out of bounds when it actually won't. In your framework, this seems
to correspond to the fact that you must verify your propositions about index
expressions. If the formulae fall into some decidable category, then they
can be verified automatically, otherwise an automatic method based on your
framework will have to give up sometimes, just like a conventional program
analysis. The formulae you give in your example are all Presburger
formulae, for which there are decision procedures, and you could use a
public domain Presburger solver like the Omega Test by Bill Pugh. Have you
though of this possibility?

Björn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] insufficiently [EMAIL PROTECTED] -- more counterintuitive stuff

2004-03-30 Thread Bjorn Lisper
Andrew Bromage:
Pattern matching on the LHS of a function definitions looks, for all the
world, like a set of rewrite rules.  That's because, in some sense, they
are.

In this definition:

f (x:xs) = 1 + f xs
f [] = []

Intuitively, the first rule should only fire if the expression being
evaluated conforms to the left-hand side.

But beware that even though they can be seen as rewrite rules, there is a
difference in that a term rewrite system allows all possible orders of
applying rewrite rules, whereas Haskell specifies a top-down order in which
the rules are tried. This can make a difference if patterns overlap, so
several patterns can match a given argument.

In the given example the patterns do not overlap though, so this can indeed
be seen as a little term rewriting system defining f.

Björn Lisper
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] Programming language shootout (completing the Haskell entry)

2004-03-29 Thread Bjorn Lisper
From: John Hughes [EMAIL PROTECTED]
Actually the cache behaviour of code generated by GHC isn't at all bad. 
I know because I ran a student project a couple of years ago to 
implement cache-friendly optimisations. The first thing they did was 
cache profiling of some benchmarks, and to our surprise we discovered 
the cache behaviour of lazy code is already pretty good. You get a lot 
of structure in the evaluation of lazy code -- it just isn't evident in 
the source code!

John, could you describe in some more detail why the code behaved well
w.r.t. cache performance? I think this is interesting, and what you say is
quite counterintuitive.

Björn Lisper
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] Making a spreadsheet with Haskell

2004-02-13 Thread Bjorn Lisper
 Hi, I would like to know how can I make a spreadsheet using Haskell
 (something like Excel, a very-reduced version, of course) 
 Do I need any kind of special library? How can I make the interface so the
 user can introduce data, select data and so on?
 
 Thanks for your help.
 Miren Cob Isasi de Vírgala

We did something called Haxcel. It is a spreadsheet-like interface to
Haskell, thus making it possible to use Haskell as a formula language. The
interface itself is implemented in Java.

See http://www.mrtc.mdh.se/projects/getProject.php3?id=0041.

Björn Lisper
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: pet project - 7 Millennium Prize problemss

2004-01-09 Thread Bjorn Lisper
--- Keith Wansbrough [EMAIL PROTECTED] wrote:
 Christopher Milton [EMAIL PROTECTED] writes:
  I think  Haskell can be used to solve several, if not all, of
  the seven problems.
  
  Now I have to decide which problem to tackle first.
 
 (a joke, I assume...)
 
 http://www.claymath.org/Millennium_Prize_Problems/
 
 1. Birch and Swinnerton-Dyer Conjecture
 2. Hodge Conjecture
 3. Navier-Stokes Equations
 4. P vs NP
 5. Poincare Conjecture
 6. Riemann Hypothesis
 7. Yang-Mills Theory
 
 Any ideas how to solve any of these, with Haskell or otherwise?

module P where

import Complexity_class(np)

p = np

:-)

Björn Lisper
___
Haskell-Cafe mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: palm?

2003-03-10 Thread Bjorn Lisper
M. Parker:
What about a port to Windows CE (i.e., for Pocket PC's). Or even better yet, 
hugs for Pocket PC!

-Matt

There is an interesting research question in here: how to design lean
implementations of lazy functional languages so they can run on small
handheld and embedded systems with restricted resources. In particular the
restricted memory available poses an interesting challenge. What I would
like to see is an implementation that is designed to be easy to port among
different handheld/embedded systems, since there are quite a few of them (in
particular there are many embedded processors). Probably a bytecode
implementation is good since byte code is compact.  Nhc might provide a good
starting point since it uses bytecode and was designed to be resource lean
in the first place. I think the people at York even did some experiments
putting it on some embedded system some years ago.

Compilation of standalone programs is also interesting since especially
embedded programs often execute in fixed environments. Lots of room for
program specialization and subsequent code optimizations here.

But I think the programming model also must be developed. One will need to
give the programmer more control over resources when necessary. This
requires operational semantics with good cost models, notoriously difficult
with lazy languages. So one will have to continue the work to integrate
eager and stateful execution into the lazy model. Furthermore, the i/o model
must be developed to accomodate the event-driven style typical for both
embedded and interactive systems. I think the functional reactive stuff from
Yale (Fran/FRP) provides a really nice high-level notation but it is hard to
make guarantees about limited resource consumption in that model. There are
models with resource guarantees (E-FRP) but they seem to limited to be
really useful in practice. One would like to cover the spectrum in-between.

Now, if one would succeed in doing all this, then there would be support for
an embedded software development model where one could first write highly
declarative executable specifications, test them for logical bugs, and then
sucessively refine the code towards a resource-lean implementation using
more and more resource-aware primitives where necessary. I think this would
be a really interesting alternative to the current imperative practice (head
on with C or assembler) and the object-oriented trend with UML/java/C++.

Björn Lisper
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: palm?

2003-03-10 Thread Bjorn Lisper
Just a side remark.
I wonder whether the byte-code approach is the best possible solution
taking into account the overload of the decoder. Why not threaded code?
The FORTH (and similar) experience, PostScript implementations, etc.
show that this paradigm may be more interesting. Anyway, when you read
for the first time the Talmud, ehmmm., I mean the description of
the STG machine by Simon PJ and others, you see that some of their
ideas are not very far from code threading.

The classical FORTH style, with the separation between tha data and
return stacks seems quite appropriate for easy implementations of
higher-order control structures. If you saw the bells and whistles
inside a FORTH processor implemented on 8bit machines, you would
agree with me.

But I do not exclude the possibility that all this has been already
discussed and rejected for some serious reasons...


Jerzy Karczmarczuk

Good remark, I just didn't think of the threaded approach. There is a reason
Forth was popular on early desktop systems. I'd like to hear some arguments
regarding the pros and cons of the threaded vs bytecode approaches!

Björn
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell


Re: Arrays vs Lists and Specialization

2003-01-22 Thread Bjorn Lisper
Matthew Donadio:
| Many spectral estimation routines are defined in terms of special
| matrices (ie, Toeplitz, etc).  Arrays defined recursively by list
| comprehensions make it easy to implement algorithms like
Levinson-Durbin
| recursion, and they look very similar to the mathematical definitions:
| 
|  levinson r p = (array (1,p) [ (k, a!(p,k)) | k - [1..p] ], realPart
(rho!p))
|  where a   = array ((1,1),(p,p)) [ ((k,i), ak k i) | k - [1..p],
i - [1..k] ]
|   rho = array (1,p) [ (k, rhok k) | k - [1..p] ]
|   ak 1 1 = -r!1 / r!0
|   ak k i | k==i  = -(r!k + sum [ a!(k-1,l) * r!(k-l) | l -
[1..(k-1)] ]) /
| rho!(k-1)
|  | otherwise = a!(k-1,i) + a!(k,k) * (conjugate
(a!(k-1,k-i)))
|   rhok 1 = (1 - (abs (a!(1,1)))^2) * r!0
|   rhok k = (1 - (abs (a!(k,k)))^2) * rho!(k-1)
| 
| OK, my question then has to do with the efficiency of lists versus
| arrays.  Do the latest compilers handle handle arrays efficiently, or
| are lists really the way to go?  If there is a performace difference,
is
| it generally big enough to warrant rewriting algorithms?

Simon:

Do not rewrite it!  This is a textbook application of Haskell arrays,
and they should work beautifully.  If not, we should fix it.  Using
lists will probably be much *less* efficient than arrays.

People often use arrays with algorithms derived from imperative
programming, which assume update-in-place.   They use (\\) for each
element, which copies the entire array, and get terrible performance.

Haskell arrays are designed to be build en-bloc, as you are doing it.
Your programs are lovely, and will not do redundant array copying.

It is, however, possible that GHC fails to deforest away all the
intermediate lists in the array comprehensions, but I think it does a
reasonable job.  You can use -ddump-simpl to see.

You could check out SAC. SAC is a functional language specialized for
arrays, with restrictions in the language to ensure efficient compilation.
The SAC compiler can apparently do a very good job on scheduling/memory
allocation as to optimize for cache performance: this is an important issue
to get performance in array computations that I believe current Haskell
implementations do not bother much about.

SAC has syntactic forms to define arrays that are somewhat reminiscent of
array comprehensions, although they are much more restricted. An interesting
route for a *real* array-optimizing Haskell compiler could be to try to find
instances of array comprehensions that can be mapped to constructs
corresponding to SAC's constructs, and then apply the compilation technology
of SAC. An alternative is to make such constructs directly available in
Haskell, so the programmer can be explicit whan performance is needed.

Homepage http://www.informatik.uni-kiel.de/~sacbase/.

Björn Lisper
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: What does FP do well? (was How to get ...)

2002-05-17 Thread Bjorn Lisper

Jerzy:
Me:
 ...sometimes the length of a list being returned from a
 function can be a simple function of the function arguments (or the sizes of
 the arguments), think of map for instance. In such cases, a static program
 analysis can sometimes find the length function. If we know thee functions
 for all list-producing functions in a closed program, then the lists could
 be represented by arrays rather than linked structures.
 
 I know Christoph Herrmann worked on such a program analysis some years
 ago. Also, I think Manuel Hermenegildo has done this for some logic
 language.

Andrew Appel wrote something about pointer-less lists as well.

What bothers me quite strongly is the algorithmic side of operations
upon such objects. 

Typical iterations map- (or zip-) style: do something with the head, pass
recursively to the tail, would demand intelligent arrays, with the indexing
header detached from the bulk data itself. The consumed part could not be
garbage collected. In a lazy language this might possibly produce a considerable
amount of rubbish which otherwise would be destroyed quite fast. The
concatenation of (parts of) such lists might also have very bad behaviour.

Can you calm my anxiety?

No, since you're right. For instance, stream-type list computations, where
list elements are used and then discarded, will not benefit from this kind
of transformation. (They will be better optimized by deforestation.)
List-to-array conversion will work best with computations where many
different elements are used many times.

Björn
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: What does FP do well? (was How to get functional software engineering experience?)

2002-05-16 Thread Bjorn Lisper

Karl-Filip:
But what I really meant is, if I may rephrase it, that imperative 
programs might often be both faster and harder to write because they
embed more information about the abblication domain. That is, if you 
code in C and want an array, you must specify its size, so you have to 
think about your program and figure out that you only need 'x' items
here, wheras in Haskell you'd use a list and never have to think about 
what the upper bound on the length of the list is.

Yes indeed. But sometimes the length of a list being returned from a
function can be a simple function of the function arguments (or the sizes of
the arguments), think of map for instance. In such cases, a static program
analysis can sometimes find the length function. If we know thee functions
for all list-producing functions in a closed program, then the lists could
be represented by arrays rather than linked structures.

I know Christoph Herrmann worked on such a program analysis some years
ago. Also, I think Manuel Hermenegildo has done this for some logic
language.

Could sized types be used for this purpose? (I must find myself some time to
read Lars Pareto's PhD thesis...)

Björn Lisper
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: partial application

2002-03-18 Thread Bjorn Lisper

Cagdas Ozgenc:
That's right. I was thinking of the following syntax. I orginally had the
idea for C++, where you can't do partial application at all.

f x y z=...

f # 3 4

omitting the first parameter, and 

Array languages with true multidimensional arrays often have a this kind of
syntax to extract subarrays, for instance A(*,J) or A(,J) to extract column
J from the matrix A. Now, if you think about arrays as partial functions from
indices to values, then row J of matrix A is precisely \I - A(I,J).

This syntax can be very convenient for array computations.

Björn Lisper
___
Haskell-Cafe mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell-cafe



Re: Isn't this tail recursive?

2002-03-12 Thread Bjorn Lisper

Hal Daume III:
Here's the basic idea.  Suppose we have the function:

 sum [] acc = acc
 sum (x:xs) acc = sum xs (acc+x)

This is tail recursive, but not strict in the accumulator argument.
...

Just a nitpick here. sum is indeed strict in its second argument (given that
(+) is strict in its first argument). That is, sum l _|_ = _|_ for all
possible lists l.

It is of course possible that the compiler you use does not detect this and
generates nonstrict code.

But I think a decent strictness analyzer should detect this. Can the problem
be that + is overloaded in Haskell, so the compiler cannot assume any
semantical properties like strictness for it?

Björn Lisper
___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: ideas for compiler project

2002-01-24 Thread Bjorn Lisper

Simon:
Lots of people have observed that Haskell might be a good scripting
language for numerical computation.  In complicated numerical
applications, the program may spend most of its time in (say) matrix
multiply, which constitutes a tiny fraction of the code for the
application. So write the bulk of the application in Haskell (where the
logic is complex but the performance doesn't matter) and then link to a
C or Fortran library to do the small part that is really compute
intensive.

More concretly, suppose you built an FFI interface to BLAS or some
readily available numerical library, and then demonstrated the utility
of the resulting beast by writing some non-trivial numerical application
in it.  

This is an interesting idea that I think can be made to work to some
extent. One issue will be to find the right tradeoff between stateful and
purely functional, i.e., allowing nice matrix-algebra based expressions
while ensuring that memory can be reused to the extent necessary. If all
matrix operations are wrapped up in some state monad, then you lose the nice
matrix algebra style. If, on the other hand, you have no way to express
updating, then you lose memory reuse (unless you have a *really* smart
compiler...).

The best tradeoff is probably to mimic a small imperative language with
clean, side effect-free matrix operations, where updates are effectuated by
explicit matrix assignments. Of course, this is possible only if the BLAS
routines are side effect-free themselves our can be wrapped up as such.

It is interesting to note that MATLAB started out exactly as a convenient
scripting language for a Fortran matrix library. MATLAB is a system for
matrix computing that contains nice graphical possibilities and a matrix
programming language. It is very popular in engineering, and is widely used
for applications like signal processing, automatic control, and PDE solving,
in particular in the early prototyping phase. Its popularity does not stem
from its speed, since it's quite slow, but rather from:
- nice graphics capabilities (plottings, etc)
- convenient matrix language (to some extent, I would add), and
- above all, lots of toolboxes, i.e., MATLAB software wrapped up for
  various applications (simulation of control systems/signal processing,
  etc.) There is a big third-party market for this kind of software.

I think MATLAB's matrix language provides about the right level of
abstraction for a high-level matrix language. You can for instance write
things like

Y = inv(A)*B

to assign to Y the solution of Ax = B. To some extent this is what you'd
like to have, On the other hand, a closer look at the language will reveal
that it has many strange and ugly semantical corners, where the
imperativeness shines through in nasty ways. This is a hindrance to
optimizing compilation, since good optimization of matrix code requires
extensive reordering transformations. Also, MATLAB is very ill-suited to
expressing block-recursive matrix algorithms, which are becoming
increasingly important in numerical computing. And, of course, there is no
decent type system, no higher order functions, etc...

So, I think it could be interesting to embed a purified version of MATLAB's
matrix language in Haskell. This would be interesting as a demonstration of
what a good matrix language could look like, but don't expect to be able to
sell it to the world since the competitors are far ahead in all other
aspects.

You'd need to find a real application though. 

There are plenty. Signal processing, automatic control, PDE solvers,...

The classical kernels
(matrix multiply, inversion etc) are precisely the things you may not
want to do in Haskell.

With the current compiler technology for Haskell, one would add. I don't
think it would be impossible to compile such Haskell programs into efficient
code. Functional languages for matrix/array computing was a quite active
research area 10-15 years ago, with efforts like Sisal and Id. These
languages were strict and first order, but you can write such programs in
Haskell. I think it would be possible to have a Haskell compiler that could
manage a subset of Haskell matrix programs quite well. (Christoph Herrmann
did some interesting work in this direction: Christoph, you're reading this
list aren't you?) Whether it would be worth the (big) effort is another
matter.

Björn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Compiler error using IO

2002-01-07 Thread Bjorn Lisper

Zhanyong Wan:
 Here here. I think that the poor compiler error messages in Haskell are a
 very major hurdle to learning the language.

Adrian Hey:
Which compiler are you talking about? Bad error messages are not a valid
critisism of the Haskell Language IMHO.

But there are language features in Haskell that makes it hard to produce
good error messages. Implicit typing, for instance: in an explicitly typed
language it is clear where in the code a type error occurs, but with
implicit typing there are often many possibilities and it is not always
evident where the culprit is. (Haskell compilers seem simply to report the
line where the unification fails. Am I right?)

Furthermore, semantically distant expressions can be syntactically close in
Haskell, so a syntax error actually gives a syntactically correct
expression.  Often this yields a type error instead, which then may show up
as an error message for some totally different part of the code...
Higher-orderness increases the risk.  An example is inadvertently writing
f g y for f (g y) which then is interpreted as (f g) y and will yield
incorrect constraints on the types of f, g, and y.

Is there any work being done on intelligent error messages for
higher-order implicitly typed languages? One could perhaps come up with
search strategies that tries to find a minimal number of program
transformations (edit distance) that yields a syntactically correct and
well-typed program. For instance, in the case of type errors one could try
to search for the minimal number of definitions that have to be removed to
make the remaining program well-typed, and then report the removed
definitions as likely sources of the errors. Has anyone done this?

Björn Lisper

___
Haskell-Cafe mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell-cafe



Re: Strange error in show for datatype

2001-10-05 Thread Bjorn Lisper

Hi again,

My question about my student's problem surely stirred an interesting and
clarifying discussion. Still, I have a question.

Reconsider the example. There's a data type 

data LispList t = Atom t | LispList [LispList t] | Str [Char]

and an instance declaration

instance Show t = Show (LispList t) where
show (Atom t) = show t
show (LispList t) = show t
show (Str t) = show t

So, hypothetically there could have been an additional, overlapping instance
declaration, say

instance Show (LispList Int) where
show (Atom t) = show t
show (LispList t) = show t
show (Str t) = Blahonga!

Then we cannot know whether show (Str HEJ) should yield HEJ or
Blahonga!. However, my student never gave such an overlapping instance,
only the first. So far as I can see there should relly be no ambiguity here!
I'd really like to know what the cause of the problem is.

Björn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Strange error in show for datatype

2001-10-03 Thread Bjorn Lisper

Hi,

For a change, a teacher asking for help on behalf of a student

I have a student who wants to emulate S-expressions of Lisp in Haskell. He
came up with the following data type:

data LispList t = Atom t | LispList [LispList t] | Str [Char]

This works just fine. He then wanted to make it an instance of Show, in
order to print values of this type:

instance Show t = Show (LispList t) where
show (Atom t) = show t
show (LispList t) = show t
show (Str t) = show t

Now, this compiles and works for some values of the type, but not for all!
Here is what happens in hugs:

hugsprompt  (Atom 1) == 1
hugsprompt  (LispList [Atom 1, Str HEJ]) == [1,HEJ]
hugsprompt  (LispList [Atom 1, LispList [Str HEJ]]) == [1, [HEJ]]
hugsprompt  (Str HEJ) == Cannot find show function
hugsprompt  (LispList [Str HEJ]) == Cannot find show function
hugsprompt  (LispList [Str HEJ,Atom 1]) == Cannot find show function

So there is a problem when the value is of form Str string or where such a
value is first in the list l in a value of the form LispList l. Oddly
enough, such values may appear at other positions without causing any
problems.

I don't think there is a bug in hugs. Similar problems appear if the Show
instance is derived, and ghc will also complain - if the definition
f = show (LispList [Str HEJ])
is compiled, for instance, the compiler will complain about ambiguous
contexts. Ghc will say

Enrico.hs:1: Ambiguous context `{Show taKi}'
 `Show taKi' arising from use of `show' at Enrico.hs:17

and hugs

Reading file Enrico.hs:
Type checking  
ERROR Enrico.hs (line 17): Unresolved top-level overloading
*** Binding : f
*** Outstanding context : Show b

So I wonder whether the infamous Monomorphism Restriction is lurking
somewhere here? But I cannot see exactly how right now. Does anyone else
have a clue?

Björn

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: A sample revised prelude for numeric classes

2001-02-12 Thread Bjorn Lisper

Tom Pledger:
Brian Boutel writes:
 :
 | Having Units as types, with the idea of preventing adding Apples to
 | Oranges, or Dollars to Roubles, is a venerable idea, but is not in
 | widespread use in actual programming languages. Why not?

There was a pointer to some good papers on this in a previous
discussion of units and dimensions:

http://www.mail-archive.com/haskell@haskell.org/msg04490.html

The main complication is that the type system needs to deal with
integer exponents of dimensions, if it's to do the job well.

Andrew Kennedy has basically solved this for higher order languages with HM
type inference. He made an extension of the ML type system with dimensional
analysis a couple of years back. Sorry I don't have the references at hand
but he had a paper in ESOP I think.

I think the real place for dimension and unit inference is in modelling
languages, where you can specify physical systems through differential
equations and simulate them numerically. Such languages are being
increasingly used in the "real world" now. 

It would be quite interesting to have a version of Haskell that would allow
the specification of differential equations, so one could make use of all
the good features of Haskell for this. This would allow the unified
specification of systems that consist both of physical and computational
components. This niche is now being filled by a mix of special-purpose
modeling languages like Modelica and Matlab/Simulink for the physical part,
and SDL and UML for control parts. The result is likely to be a mess, in
particular when these specifications are to be combined into full system
descriptions.

Bjrn Lisper

___
Haskell-Cafe mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell-cafe



Re: Revamping the numeric classes

2001-02-11 Thread Bjorn Lisper

Marcin Kowalczyk:
Me:
 No, the transformation is a single step procedure where a term
 is transformed into a typeable term (if possible) with a minimal
 amount of lifting. You don't compose transformations.

So functions implicitly lifted can't be used in the same ways as
functions originally defined as lifted (namely, they can't be lifted
again)... This is bad.

Functions themselves are never lifted. They just appear to be lifted when
applied to arguments of certain types, and then the function application is
always statically resolved into an expression where the function has its
original type.

 Due to the principle of minimal lifting (which implies already
 well-typed terms should not be transformed) a call to a polymorphic
 function should not be transformed unless there is a dependence
 between the type variable(s) of the function and of the argument(s)
 in the application.

Does
f x y = (x, x + y)
has type
Num a = a - a - (a, a)
and thus it cannot be used on the type
Int - [Int] - (Int, [Int])
even though if its body was inlined into an expression requiring that
type, it could (by lifting x+y to map (x+) y)?

Your function has its polymorphism constrained to the Num class. So we could
allow elemental overloading (on the uncurried form of f) as long as [Int] is
not an instance of Num.

Yes, if [Int] is made an instance of Num then the meaning of calls to f on
lists will change from the elemental meaning to the meaning defined through
the instance declarations for [Int]. This can surely be a problem in some
cases. But this is not a property of the elemental overloading mechanism per
se, but rather that we would have two different overloading mechanisms in
the language powerful enough to specify conflicting overloading.

BTW, the type signature for your "lifted f" (if it existed) should be
(Int,[Int]) - [(Int, Int)]. See second example below.

You can't lift arbitrary function of type Int - Int - (Int, Int)
into Int - [Int] - (Int, [Int]) without knowing its defintion.
Try it with g x y = (y, x).

Consider uncurried g: g (x,y) = (y,x) and assume it has an explicit type
declaration to (Int,Int) - (Int, Int) (so it's not polymorphic).
if x :: Int and l :: [Int], then
g (x,l) - zipWith (g.(,)) (repeat x) l :: [(Int,Int)].
The rewrite of the overloaded application is guided only by type
information. (Again note only the application of g is rewritten, neither g
itself nor its type does change.)

Suppose there is a function
fancyPrint :: Printable a = [a] - IO ()
which applies some fancy printing rules to a list of printable values.

A programmer knows that this function can be used on lists as well
as on single elements, because those elements will be promoted to
single-element lists as necessary. So far so good.

The rules I have sketched so far only promote a value in connection with
elemental overloading. A good example is g (x,l) above. Here, g is
elementwise applied to l and in the process x becomes promoted into
(repeat x), similar to the original scaling example a*x where a is a scalar
and x a matrix. So with these rules alone fancyPrint 17 would not be
rewritten into fancyPrint (repeat 17). But the rules can of course be
extended to cover this case.

Then he applies it to a single String. Guess what? It is not
promoted to a single-element list, but each character is printed
separately. Oops!

Which is the original meaning of fancyPrint applied to a string. Why "oops"?
A programmer must be aware of the meaning of function he writes. fancyPrint
will always be a function over lists, no matter whether its use is
overloaded on arguments of other types or not.

 It's not enough, because the least lifted type is not the most general
 answer. Answers for different amounts of liftedness are incompatible
 with that answer - they are not its instances as in the HM typing.
 
 It does not matter that they are not instances.

It does. It's not enough to check that there exists a set of places
to insert map or zipWith which transforms what is written to what I
need. Because there can be a different, incompatible set of places,
which is considered "better" by the compiler and my set is not
obtainable from what the compiled has done. See the first example
above.

(I think your example was broken, but in principle you're right.) Of course,
the use of the overloading I have described (and any kind of overloading) is
justified only if the overloading matches the intuition of the programmer.
If it misleads you then it is harmful. Regarding elemental overloading, my
experience is that data parallel programmers quickly develop a strong
intuition for it. What I have seen through examples is that elemental
overloading using the rules I have sketched and the "minimal lifting"
principle always seems to produce the intuitively correct meaning, also in a
language like Haskell. If the produced result is the "right" one, then the
fact that other possible transformations produce terms with incompatible
types is 

Re: Types from values using existential types?

2001-02-10 Thread Bjorn Lisper

I recently had an idea to allow one to create types from values.  This
would have many uses in Haskell; e.g., it would be very nice to have a
type of nxn matrices for n that are not necessarily statically
determined.  (It is easy to create a type of matrices and check that
the sizes are compatible at run time; the point is to do more checks
at compile time.)  Of course, one has to be careful to preserve
decidability of type checking.
.
Are there any pointers to previous work I should look at?

If type systems for matrices are of interest for you then I think you should
check out Barray Jay's work on the language FiSH.

Bjrn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Revamping the numeric classes

2001-02-09 Thread Bjorn Lisper

 I see. So you can transform arbitrary function of type a-b-c
 to a function of type [a]-b-[c], by applying
 \f x y - map (\z - f z y) x
 and similarly a-b-c to a-[b]-[c]. But then there are two ways of
 transforming a-b-c to [a]-[b]-[[c]

 There should be no transformation to type [a]-[b]-[[c]] in this case.

Wait wait wait. You told that a-b-c is convertible to [a]-b-[c]
for *any* a,b,c. Now I have x = [a], y = b, z = [c] and use the
transformation x-y-z to x-[y]-[z] and obtain [a]-[b]-[[c]].

Both steps are legal, so their composition must be legal, or I don't
like this Haskell-like language anymore.

(You never seem to have liked it much ... :-)

No, the transformation is a single step procedure where a term is
transformed into a typeable term (if possible) with a minimal amount of
lifting. You don't compose transformations.

Let us write [a]^n for the type of n-deep lists of lists of a. If
f :: a - b - c, x :: [a]^m, and y :: [b]^n, then
f x y is transformed into a term with type [c]^max(m,n). This is the minimal
lifting necessary to obtain the correct elementwise application of f.
If m  n then promotion will take place on the first argument, if m  n on
the second, and if m = n then there will be an elementwise application
without promotion.

Unless you say that a-b-c is convertible to a-[b]-[c] *except*
when a is a list. Then it's bad again. There should be no negative
conditions in the type system! Moreover, in a polymorphic function
you don't know yet if a will be a list or not.

Due to the principle of minimal lifting (which implies already well-typed
terms should not be transformed) a call to a polymorphic function should not
be transformed unless there is a dependence between the type variable(s) of
the function and of the argument(s) in the application. Such dependencies
could possibly occur in recursive calls in function definitions. Consider,
for instance the (somewhat meaningless) definition

f x y = head (f [x,x] y)

During type inference, f will first be assigned the type a - b - c.  In
the recursive call, f will be called on arguments with types [a] and b. This
causes a lifting to occur, where f is elementwise applied to [x,x] with
promotion of y. The transformed definition becomes

f x y = head (zipWith f [x,x] (repeat y))

On the other hand, if f somewhere else is applied to some other arguments,
with types not containing a, then no transformation of that call will occur.

There are no full and partial applications because of currying.
It's impossible to say when you should consider a function as a
multiparameter function. There are only single-argument functions.
So you would have to say that some rule apply only *unless* the result
has a function type, which does not work again.

Touch! To keep the discussion simple I have kept multiparameter functions
curried, but you nailed me. Yes, there will be ambiguities if you allow
overloading on other than the first argument in a curried definition (since
there really is only one argument). So for a function f :: a - b - c we
should only allow elementwise overloadings corresponding to functions of
types [a]^n - [b - c]^n. Elementwise overloading on multiparameter
functions must appear only on their uncurried forms, so only if
f :: (a,b) - c then we can allow transformations of calls corresponding to
type signature ([a]^m,[b]^n) - [c]^max(m,n).

(This would give problems with elementwise overloading of arithmetic
operators in Haskell, since these are curried. But, as I said earlier, I'm
not proposing to actually extend Haskell with this overloading, I'm only
discussing the concept as such in the Haskell context.)

With your rules a programmer writes code which is meant to implicitly
convert a value to a single-element list, because something tries
to iterate over it like on a list. Unfortunately the element happens
to be a string, and he gets iteration over its characters. And if it
works the other way, another programmer meant iteration over characters
and got iteration over a single string. You can't tell which was meant.

I don't think there will be any ambiguities here. The overloading is
resolved statically, at compile-time, for each call to the function. Calls
to polymorphic functions are not transformed (except for cases like I showed
above).

 Of course the type/term transformation system must have the property that
 if different transformations can yield the "best" type (wrt liftedness),
 then the transformed expressions should be semantically equivalent.

It's not enough, because the least lifted type is not the most general
answer. Answers for different amounts of liftedness are incompatible
with that answer - they are not its instances as in the HM typing.

It does not matter that they are not instances. Each call is transformed
statically, separately. The liftedness ordering is used only to direct the
resolution of the overloading, so we pick the minimal lifting (the others
are not interesting). The overloaded function 

Re: Revamping the numeric classes

2001-02-08 Thread Bjorn Lisper

 Also, what is the inferred type of, for example
 f x y = x + length y
 ? It can be
 Int - [a] - Int
 [Int] - [a] - [Int]
 and neither is more general than the other. And this is a simple
 function.
 
 Int - [a] - Int, since this is the type it will get in the original type
 system.

So I can't apply f to lists, but I could if I inline its body. This
means that I cannot arbitrarily refactor a piece of code by moving
parts of it into separate definitions: subexpressions are given
some extra meanings only if they are physically placed in certain
contexts. This is bad.

This is a misunderstanding. the transformation of f l y , where l :: [Int]
for instance, should depend only on the type of f and not its definition.
It is the call to f, not f itself, that becomes transformed. No inlining
takes place.

Ah, so what uses of f are correct depends on its definition, not type!
Sorry, this is way to radical.

Types exist to formalize possible ways a value can be used. HM allows
to determine most general types variables in a let-block (or: of a
module) before their uses, so separate compilation is possible. In
your system typechecking of a function's definition is done each time
it is used!

No. See above.

Bjrn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Revamping the numeric classes

2001-02-08 Thread Bjorn Lisper

 It is quite similar in spirit to the concept of principal type in
 Hindley-Milner type systems. An expression can have many types but
 only one "best" (most general) type in that system.

Now, I'm not any kind of expert on this, but isn't the most general
HM type one that encompasses the others, and *not* one out of a set of
ambigous (and mutually exclusive) types?

In a sense. You define a partial order on types by a  a' (a more general
than a') if there is a substitution s of type variables such that
a' = sa. The interesting property of HM type systems is that for each term t
and all type judgements t:a that can be derived, there is a type judgement
t:a' such that a'  a. a' is called the most general type of t.

What I suggested was to define a different relation between types, measuring
"relative liftedness". We can call it "". Now, if it is the case that for
all judgements t - t':a in the type system I sketch, there is a judgement
t - t'':a' where a'  a, the we can select the transformation to be
t - t''. t'' will then have a "most general type" among the possible
transformed terms, but wrt  rather than .

Ambiguity between types depends on the ordering between types that you
consider!

Bjrn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Revamping the numeric classes

2001-02-08 Thread Bjorn Lisper

I see. So you can transform arbitrary function of type a-b-c
to a function of type [a]-b-[c], by applying
\f x y - map (\z - f z y) x
and similarly a-b-c to a-[b]-[c]. But then there are two ways of
transforming a-b-c to [a]-[b]-[[c]] and the order of applying the
former transformations does matter. Worse: a third way is to apply zipWith
and then promote the result to a single-element list. Or maybe map the
result to a list of single-element lists...

There should be no transformation to type [a]-[b]-[[c]] in this case.
If f is applied to arguments of type [a] and [b] then this should be
interpreted as the elementwise application of f to the two argument lists,
and the result type should then be [c]. Note that [a]-[b]-[[c]] is
"more lifted" than [a]-[b]-[c].

Elementwise application to one argument should transform to map, of several
arguments to zipWith with appropriate arity.

It is easier to see how it should work if we skip lists, so we don't have to
deal with maps and zipWiths and other list functions.  Let us consider
elementwise application of f over indexed entitites. For simplicity we
consider functions as our indexed entities, but it could as well be arrays.
With f as above, then f x y should be transformed to:

(1) x :: d - a, y :: b yields \i - f (x i) y
(2) x :: a, y :: d - b yields \i - f x (y i)
(3) x :: d - a, y :: d - b yields \i - f (x i) (y i)

Here (3) is "full" elementwise application, and (1) and (2) are "partial"
elementwise applications where the unlifted argument can be seen as
promoted.  If you have list instead of functions, then the transformation
should insert list primitives with the corresponding effect.

Sorry, IMHO it's ambiguous as hell except very simple cases.

Of course the type/term transformation system must have the property that
if different transformations can yield the "best" type (wrt liftedness),
then the transformed expressions should be semantically equivalent. I believe
a type/term transformation system with this property can be designed, but
the details remain to be worked out.

Bjrn Lisper

(Is this discussion still of interest to the Haskell list members? Or should
we take it offline?)

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Revamping the numeric classes

2001-02-07 Thread Bjorn Lisper

Marcin 'Qrczak' Kowalczyk:
Me:
 I'd like to point out the connection between the use of +, - on vector
 spaces and * for scaling with features in some data parallel languages.  In
 these languages, writing a + b where a and b are arrays of numerics is
 interpreted as elementwise addition of a and b. This features generalises to
 other operations than +, -, other types than numerical ones, and other data
 structures than arrays.

This is what I dislike. It's implicit fmap / zipWith / etc. But it only
works as long as there is only one meaningful way to insert these fmaps.
When I apply length to a list of lists, is it the length of the whole list
or a list of lengths of its elements? So there must be explicit ways of
specifying the amount of fmaps, and one cannot assume that they will be
always placed automatically. It might be convenient for very specific
types computation but is not a working general idea.

A natural principle to adopt is that an already typeable expression should
not be transformed. This will for instance resolve the ambiguity in the list
of list example: if l :: [[a]] then length l is already well-typed and
should not be transformed into map length l.

 Furthermore, some of these languages support "promotion": "lifting" a
 "scalar"-typed expression, appearing in a context where an array (say)
 is expected, into an array with suitable dimensions containing copies
 of the scalar.

Again, Haskell does not have subtyping. It is not compatible with type
inference - it can only work in poor languages which require an operation
to be fully applied where it is used, and either don't have static types
or require them to be specified explicitly.

I am not so sure about this. Could you exemplify?

Note that you can do some of this overloading already within Haskell's class
system. For instance, one can make lists of Nums into Nums by declaring

instance (Num a) = Num [a] where
x + y = zipWith (+) x y
x * y = zipWith (*) x y
...
fromInteger x = repeat (fromInteger x)
...

Now, if x and y are lists of Nums, then 2*x + y becomes

zipWith (+) (zipWith (*) (repeat fromInteger 2) x) y

In Haskell trying to implement
such overloading would be too clumsy and would not work as expected in all
cases, so better don't go this way.

I should point out that I didn't suggest adding this overloading in Haskell,
I was merely pointing out the connection between vector space syntax/scaling
and features in data parallel languages.

Bjrn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Revamping the numeric classes

2001-02-07 Thread Bjorn Lisper

Marcin Kowalczyk:
Me:
 A natural principle to adopt is that an already typeable expression should
 not be transformed. This will for instance resolve the ambiguity in the list
 of list example: if l :: [[a]] then length l is already well-typed and
 should not be transformed into map length l.

So there are ways to interpret an expression which are not chosen
only because some other way is a better match? This is very dangerous
in principle.

Two interpretations of a code are "correct", but one is "more correct"
than the other.

It is quite similar in spirit to the concept of principal type in
Hindley-Milner type systems. An expression can have many types but only one
"best" (most general) type in that system.

Also, what is the inferred type of, for example
f x y = x + length y
? It can be
Int - [a] - Int
[Int] - [a] - [Int]
and neither is more general than the other. And this is a simple
function.

Int - [a] - Int, since this is the type it will get in the original type
system.

The types you mention are incomparable w.r.t. the usual "more
general"-ordering on types, but one could consider also other orderings. For
the types you give, the second is more "lifted" than the first in that it
contains [Int] in places where the first type has Int. One can define a
"liftedness" order on types in this vein.

(OK, so one would need to go through the formalities and prove that there
are "principal types" w.r.t. this relation between types, and that this new
principal type concept is not in conflict with the old one. I cannot say for
sure that it works.)

I should be more specific about what a type system could look like that
implements this kind of overloading. It could be a coercive type system,
with judgements of the form

t - t':a

where t, t' are terms, a is a type, and t:a is a correct judgement in the
original type system. So the type system not only gives a type but also a
transformation that resolves the overloading into a well-typed term.

 Again, Haskell does not have subtyping. It is not compatible with type
 inference - it can only work in poor languages which require an operation
 to be fully applied where it is used, and either don't have static types
 or require them to be specified explicitly.
 
 I am not so sure about this. Could you exemplify?

Sorry, I don't have a concrete example in mind. How to infer types when
implicit conversions are possible anywhere? The above function f can
be applied even to two numbers (because the second would be promoted
to a list of length = 1), so what is its inferred most general type?

Int - [a] - Int. If f is applied to some arguments with other types for
which the overloading is defined, say f l1 l2 where l1 :: [Int] and
l2 :: [a], then the term f l1 l2 would be transformed into a well-typed term
but the type of f itself would not change.

Again, arbitrarily choosing one of the alternatives basing on some
set of weighting rules is dangerous, because a programmer might mean
the other alternative - there is no simple way to ensure that the
compiler interprets it in the same way as I wanted. It's not enough
to check that all types match modulo conversions - I must carefully
check that no "better" interpretation is possible.

I surely agree that this kind of overloading should be used only when it is
in accordance with the intuition of the programmer. This could, for
instance, imply restrictions to certain types or operators.

Bjrn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Specifications of 'any', 'all', 'findIndices'

2001-01-23 Thread Bjorn Lisper

Koen:
The definitions in the Haskell report are a *specification*,
not an implementation. It probably depends on the compiler
which particular implementation runs faster.

Therefore, the Haskell report provides a clear (yes, this is
debatable) *possible* implementation, and the compiler
writer is free to implement this in whatever way (s)he
likes. As long as the implementation has the same functional
behavior as the specification in the report.

Hear, hear. What I in turn would like to add is that specifications like 

any p = or . map p

are on a higher level of abstraction than definitions like

any p [] = False
any p (x:xs) = p x || any p xs

This makes it easier to find different implementations, which makes it
easier to adapt the implementation to fit different architectures. The first
specification is, for instance, directly data parallel which facilitates an
implementation on a parallel machine or in hardware.

Bjrn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Specifications of 'any', 'all', 'findIndices'

2001-01-23 Thread Bjorn Lisper

Pardon?
map is data parallel. foldr not so obviously...

Sorry, a slip on my behalf: foldr in general is not data parallel, but if
the function being folded with is associative then foldr can be implemented
by a parallel (balanced binary-tree) reduction in time O(log n), where n is
the length of the list.

A compiler needs the information that or is a fold over an associative
operation in order to employ such a parallel implementation.

Bjrn Lisper

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Higher-order function application

2000-08-23 Thread Bjorn Lisper

Tim Sweeney:
Is this "higher order function application" a useful notion, and does any
research exist on the topic?

The answer to the first question is "yes, when it matches the intuition of
the programmer". Your two first examples:

cos+sin-- intent: \x-((cos x)+(sin x))
cos(sin)   -- intent: \x-cos(sin(x))

have equivalents in Fortran 90 and HPF, although with arrays rather than
functions. For instance, one can write "A+B" to mean an array with value
A(I)+B(I) for all indices I, and A(B) for the array with elements A(B(I))
(provided B is an integer array whose elements all are valid indices for
A). This feature is widely used in array languages, where it is seen as an
intuitive and convenient notation to express array operations. I definitely
believe it could be useful also for operations over other data structures.

The answer to the second question is "surprisingly little". There is, for
instance, no formal description to be found of the Fortran 90 array
operations and how they type.  But it is quite straightforward to define
type systems and type checking algorithms for this, when the language is
explicitly typed. One example is

@InProceedings{Thatte-ScalingA,
  author =   {Satish Thatte},
  title ={Type Inference and Implicit Scaling},
  booktitle ={ESOP'90 -- 3rd European Symposium on Programming},
  editor =   {G. Goos and J. Hartmanis},
  number =   432,
  series =   {Lecture Notes in Computer Science},
  year = 1990,
  publisher ={Springer-Verlag},
  address =  {Copenhagen, Denmark},
  month =may,
  pages ={406--420}
}

where a type system for an APL-inspired overloading in an FP-like language
is described. This approach is based on subtyping.

A student of mine is pursuing another, more direct approach, where a
coercive type system is used to resolve the overloading at compile time
through a combined rewrite and type check. He did this for an explicitly
typed variant of Core ML, and this is reported in his Licentiate thesis
("file://ftp.it.kth.se/Reports/paradis/claest-licthesis.ps.gz"):

@PHDTHESIS{claest-lic,
AUTHOR = {Claes Thornberg},
TITLE = {Towards Polymorphic Type Inference with Elemental Function 
Overloading},
SCHOOL = it,
ADDRESS = {Stockholm},
YEAR = {1999},
TYPE = {Licentiate thesis},
MONTH = may,
NOTE = {Research Report } # rep-id # {99:03}
}

@STRING{it = "Dept.\ of Teleinformatics, KTH"}

@STRING{rep-id = "TRITA-IT R "}

When the type system is implicit (inference rather than checking), however,
less is known. You can do some tricks with the Haskell class system (for
instance, defining functions between instances of Num to be instances of
Num themselves, which then lets you overload numerical operations like "+")
but this solution has some restrictions and is also likely to lead to
run-time overheads. We would like to have something better.

Finally, there is an interesting discussion of this overloading business,
for array- and data parallel languages, in

@ARTICLE{Sip-Blel-Coll-Lang,
AUTHOR = {Jay M. Sipelstein and Guy E. Blelloch},
TITLE = {Collection-Oriented Languages},
JOURNAL = {Proc.\ {IEEE}},
YEAR = {1991},
VOLUME = {79},
NUMBER = {4},
PAGES = {504--523},
MONTH = apr
}

For instance, they bring up the possible conflicts which may occur when
trying to resolve this overloading for operations over nested data
structures. (A witness is length l, where l :: [[a]]: should it be just
length l, or resolved into map length l?)

Björn Lisper




Re: What happened to the GRIN project?

2000-06-19 Thread Bjorn Lisper

 1) are anyone pursuing this line of work, and

Sadly, Urban left us for industry (Carlstedt Research and Technology), where
Thomas Johnsson is also on sabbatical at the moment. So nobody here is
pursuing this at the moment.

 2) is the software available?

Ask Urban: [EMAIL PROTECTED] ought to reach him still.

My former student Karl-Filip Faxen did his PhD thesis on very similar
compilation methods for lazy languages (Karl-Filip, do you follow the
Haskell mailing list?), and he also got impressive speedups for a set of
admittedly small benchmarks.  His compiler does not implement full Haskell,
but there should be no problems in principle to extend it to full Haskell
and his compilation techniques are in no way dependent on this
restriction. He can be reached at [EMAIL PROTECTED] He is still working on his
compiler and he has a paper in the upcoming ICFP conference.

I think Karl-Filip's and Urban's methods are very interesting and the way to
go for efficient compilation of lazy languages. The key to cope with large
programs is probably to have an intelligent strategy for function cloning,
which clones functions to increase the potential to optimise by
specialisation, but only for calls where there is a potential to gain from
this in order to avoid the potential code explosion.

Björn Lisper




Re: == and hyperstrictness

2000-03-22 Thread Bjorn Lisper

Suppose Sven implements his `len' function as above, and furthermore
implements a library which depends on this function being hyperstrict.
Suppose next that I implement an instance of `==' that returns `True' without
evaluating the arguments, and then finally suppose a third programmer called
say Joe comes along and uses my type with Sven's library.  If it breaks, who
is to blame?

I think the cleanest solution is to add a method "hseq" to the Eval class,
which is similar to seq but evaluates its first argument hyperstrictly
before returning its second. Then Sven can write
len :: Eq a = [a] - Int
len [] = 0
len (x:xs) = x `hseq` (1 + len xs)

We did this in "Data Field Haskell" - a dialect of Haskell for
collection-oriented programming which we have designed and made a prototype
implementation of - where we happened to need this functionality. (Those of
you who are interested can have a look at
http://www.it.kth.se/labs/paradis/dfh/. I plan to make a "public release" of
of the system and announce it here, but there are still some problems with
the installation scripts so I have put the release on hold until we have
sorted these problems out. For now, download at your own risk :-)

Björn Lisper




Re: == and hyperstrictness

2000-03-22 Thread Bjorn Lisper

Me:
 I think the cleanest solution is to add a method "hseq" to the Eval
 class, which is similar to seq but evaluates its first argument
 hyperstrictly before returning its second.

Marcin 'Qrczak' Kowalczyk:
You mean "implicit Eval"? Then I would certainly not call this solution
"clean". Even the full polymophism of seq is considered controversial,
and the Eval class was explicit before.

I don't know about full polymorphism (I guess hseq would not work well for
function-typed arguments, for instance), but I know there are situations
where it can be important from a practical perspective to add varying
degrees of strictness explicitly. Then I think the cleanest solution is to
have explicit operations which give you this, with a clearly stated
semantics, rather than the "x==x" kind of hack.

Björn Lisper




Re: definition domain extension proposal

2000-01-31 Thread Bjorn Lisper

Sergey:
To separate a sublanguage of Haskell given by the corresponding
compilation key  (say,  -domExt )  or, maybe, by the compiler pragma.

This sublanguage allows the compiler to replace any function  f  with
the function  f'  being the extension for  f.

This was discussed on the list a while ago, right? I remember Arvind arguing
for this kind of transformation. I also think there is a need for it, or
rather for a way to more in general tell the compiler that it is OK to use
"almost correct" transformations, which need not be just extending the
domain of a function. An example is to let the compiler consider addition of
floating-point numbers to be associative (which it really is not, just
approximately), which makes it possible to parallelise folds over it. This
transformation can be very important in HPC applications, but its
application requires the knowledge that the numerics doesn't go wrong.

An interesting question is how to specify these kinds of transformations for
the compiler in a flexible way.

Björn Lisper



Re: fixing typos in Haskell-98

2000-01-24 Thread Bjorn Lisper

 Take and drop
 [..]
 I can see three alternatives:
 
 (A) Make them defined for any n.  If n  0, do something reasonable:
   take:   give empty list
   drop:   give whole list

 (B) Make them defined for n  length xs, but fail for n  0.

 (C) Status quo
 
 PROPOSAL: Use alternative (A)

I vote the same. (A) is in accordance with the view of a list xs as a
function from the interval {0,...,length xs - 1} (with [] as a function from
the empty set {}, and {a,...,b} = {} iff a  b). Then take n xs selects the
defined elements of xs from {0,...,n - 1} and drop n xs those from
{n,...,length xs - 1}. If some other alternative is chosen then this simple
view breaks down.

Björn Lisper



Re: What is a functional language?

1999-09-28 Thread Bjorn Lisper

Me:
 Yes, it makes a lot of sense. The parallel or above is a classical example
 of a function for which there exists no semantically correct sequential
 evaluation order of its arguments (i.e., an evaluation order where an
 argument is evaluated in full before the evaluation of the next argument
 starts). Therefore, it has usually been discarded as infeasible to implement
 since the in the worst-case scenario its evaluation may require that you
 spawn off an exponential number of concurrent threads (imagine a function
 calling the parallel or recursively on both sides).

Adrian Hey:
This is the biggest problem I think.
Implementation would be very difficult. It requires a compiler smart
enough to only start separate threads if it's going to make some
difference to the termination properties, and a very efficient
implementation capable of supporting an arbitary number of threads.
I don't see the potential problem with infinite nos. of threads as
a reason not to do this though. Haskell as it stands provides plenty of
opportunity to screw up with infinite things :-)

This is not to discourage you (I think your idea is quite interesting), but
the parallel or actually provided a big headache to early researchers in
semantics. It pops up in the standard functional domains used for
denotational semantics and its non-sequential nature was seen as kind of
unnatural. (For instance, it cannot be encoded in the lambda calculus.)
Great efforts were spent do define function domains where non-sequential
functions like the parallel or did not exist. I don't think these efforts
really ever succeeded.

I have always felt that these efforts were somewhat misguided.
(Theoreticians out there, please don't kill me!) There is nothing strange
with parallel threads, or a function that requires parallel threads for its
faithful implementation. But there might be a serious problem with
efficiency. There have been attempts in this direction: Early
implementations of the language Oz, for instance, used a parallel strategy
where a thread was spawned off for each call. I think it was done this way
just because it was simple to implement (Oz is concurrent, so they needed to
support parallel threads anyway.) In later version of Oz this strategy has
been abandonded, though., for efficiency reasons I think.

Björn Lisper






Re: What is a functional language?

1999-09-27 Thread Bjorn Lisper

Adrian Hey [EMAIL PROTECTED]:
The issue I'm thinking about is equational reasoning and program
transformation. For a lazy (non-strict) language, transformations
are constrained so that a function definition which is non-strict
in an argument is never transformed into one which is strict in the
same argument. (Or so I believe, please correct me if I'm wrong).

Well, if you want the transformations to preserve the semantics, yes.

(N.b. that a function with a nonstrict argument may be called in an
environment where we know something about the argument, for instance, that
it is always evaluated, which then makes it correct to replace *that call*
to the function with a call to a transformed function with a strict
argument.)

This seems completely contrary to normal equational reasoning (which is
one thing functional languages are supposed to support), where we aren't
so constrained. 

No. Equational reasoning simply means that we use equations which are valid
to transform expressions.  When used for program transformations, the set of
valid equations is determined by the semantics of the language. This set of
equations will change depending on whether the language is strict or
nonstrict. For instance, a logical or which is strict in both arguments will
be commutative and associative, while a nonstrict or with a left-to-right
evaluation order of its arguments will be non-commutative but still
associative.

Nonstrict languages actually have the property that function definitions can
be seen as equations.  I would say this is what makes the language
referentially transparent! This means that a function call can always be
symbolically unfolded without altering the semantics. Strict languages do
not have this property in general. So in this sense nonstrict languages are
more amenable to equational reasoning!

Note that if we want the transformations to preserve the semantics of the
language w.r.t. termination we will have to deal with bottom in equations
also in strict languages, since also strict languages contain certain
operations with nonstrict semantics (e.g., conditionals). Of course, we can
consider a coarser semantics without the bottom, where we do not distinguish
functions w.r.t. termination properties, and then have a different set of
equalities: a good example is x - x = 0 which is valid if bottom is
discarded but not valid if it is included. (I believe it may be this kind of
discrepancy which has lead you to think that nonstrict languages do not
permit equational reasoning.)

So is it unfair to say that as soon as issues like strict vs. non-strict
semantics become an important, the language is no longer purely functional?

It is not only unfair, it is wrong.

Doesn't having to treat _|_ as a value spoil equational reasoning?

No, see above.

Björn Lisper






Re: What is a functional language?

1999-09-27 Thread Bjorn Lisper

On Mon 27 Sep, Bjorn Lisper (i.e., me) wrote:

 For instance, a logical or which is strict in both arguments will
 be commutative and associative, while a nonstrict or with a left-to-right
 evaluation order of its arguments will be non-commutative but still
 associative.

Adrian Hey:
But should we let such operational issues muddy the waters?

Is a function..

 1- A mapping between input values and output values (which don't include
_|_). I imagine this is what mathematicians would call a function.

or..

 2- A method of computation. For all its abstraction, as far as Haskell
is concerned this has to be the correct way to view a function. This
is what makes Haskell a _programming_ language, rather than a tool
for reasoning about the properties of functions.

In my example, it's both. On one hand, we can give a purely equational
specification of the nonstrict or I sketched above (I just give the
nonstrict part):

true or bot = true
false or bot = bot
bot or x = bot,  for all x

On the other hand, it is well known that one particular reduction strategy
that implements this semantics faithfully is the classical left-to-right
strategy.

So, I guess the answer is.. Yes, we do need to let such operational
issues muddy the waters. What worries me here is that there might be
some unjustifiable assumptions about the nature of the machine which is
performing those operations. In particular by talking about things
like 'reduction order' and 'order of pattern matching' aren't we
assuming a sequential machine. If so, should such considerations be
allowed to 'pollute' the semantics of a general purpose high level
programming language.

We are not assuming a sequential machine. The equational semantics of "or"
above does for instance allow that we speculatively fork off a thread
evaluating the second argument before we know the value of the first
argument, if we just delay the return of the answer until the first argument
is fully evaluated. On the other hand, one must keep some operational
semantics in mind when specifying a more abstract semantics, so one does not
end up with a language which is very expensive or impossible to implement on
the machines at hand.

 Nonstrict languages actually have the property that function definitions can
 be seen as equations.

Almost, but not quite, as Fergus Henderson noted.

Well, as Fergus pointed out, syntactic sugar like definitions by patterns
may have to be removed first.



What started me thinking about this is the 'World as Value' thread on the
Clean discussion list, where I tried (unsuccessfully I suspect) to make the
case for using a concurrent non-deterministic machine as the basic model
for I/O.

Then I started thinking about how one would compile function definitions
into 'computational actions' on such a machine (a Haskell compiler does
this for sequential machines) and realised that this would give that
language subtly different semantics wrt the way _|_ was treated. I don't
know what the proper name for such semantics is, so I'll call it
'concurrent non-strict' semantics as distinct from 'sequential non-strict'
semantics.

Do you think about such semantics as the "parallel or", which has its
strictness properties defined by:

false or bot = bot or false = bot
true or bot = bot or true = true

?

I think concurrent non-strict semantics restores many (perhaps all?)
useful transformations. An obvious example being it restores
transformations like..
  a  b = b  a 

Does this make sense?

Yes, it makes a lot of sense. The parallel or above is a classical example
of a function for which there exists no semantically correct sequential
evaluation order of its arguments (i.e., an evaluation order where an
argument is evaluated in full before the evaluation of the next argument
starts). Therefore, it has usually been discarded as infeasible to implement
since the in the worst-case scenario its evaluation may require that you
spawn off an exponential number of concurrent threads (imagine a function
calling the parallel or recursively on both sides).

Björn Lisper






Re: Non-strictness vs. laziness (was RE: Sisal)

1999-09-24 Thread Bjorn Lisper

Joe Fasel wrote:
 Actually, I think we were originally thinking of laziness, rather
 than nonstrictness, and weren't considering languages like Id as
 part of our domain, but Arvind and Nikhil (quite correctly) convinced
 us that the semantic distinction of strictness versus nonstrictness
 should be our concern, rather than the operational notions of
 eagerness and laziness.

"Frank A. Christoph" [EMAIL PROTECTED]:
Please elucidate. Where does this difference become important? What impact
did it have on the language?

It is definitely important in parallel processing, where you may want to
spawn off activities speculatively, to utilize idle processors, and kill off
these activities later if it is discovered that their results are not
needed. This yields nonstrict behaviour if implemented correctly, but it is
not lazy.

Björn Lisper





Re: Cryptarithm solver - Haskell vs. C++

1999-09-22 Thread Bjorn Lisper

"Manuel M. T. Chakravarty" [EMAIL PROTECTED]:
* While Sisal is arguably nice than Fortran, it doesn't
  really provide a new killer feature - rewriting all this
  Fortran code, just for getting nice programs is maybe not
  enough of an incentive.

As I remember it, a main argument for Sisal was that the freedom of side
effects would simplify the automatic parallelisation. So one important
percieved incentive was to actually get better performance than from
automatically parallelised Fortran.

Björn Lisper





Re: more on Rules

1999-05-07 Thread Bjorn Lisper

The optimization " n+1  n == True " is extremely useful in array
subscript analysis, bounds checking etc. and is correct as long as n
terminates (is not bottom). The cases where this optimization is used
most often, n usually terminates. A more diligent compiler would use
the optimization only if it could prove that n does terminate.  I will
take my chances with a fast but less diligent optimizing compiler
which in some diabolical cases produces 3 when it should not have
terminated.

Both the Id compiler and now the pH compiler follow this philosophy.
May be we should let an implementation do whatever it pleases in case
of an error. If we regard non-termination as an error then producing a
value in case of non-termination may not seem so bizarre. However,
there are many cases where I really do want my errors reported. I
think such compiler/language decisions should be taken on pragmatic
grounds.

I think it is important that these "almost always semantics-preserving"
optimizations are not carried out silently. If they are, then programmers
may start to rely on them and then their programs may break if they are
compiled with a compiler that does not perform the optimization. On the
other hand it is perfectly true that many useful optimizations have this
character. So one would like some kind of user control.

I think that sometimes the actual specification one has in mind is not as
rigid as the one that actually is implemented by the code. The problem we
had here earlier, about reordering reductions where the operations are only
"almost AC" (e.g., floating-point arithmetic), is similar. Often one
wouldn't care about the numerical variations resulting from different
reduction orders, but sometimes they could matter.

Bjorn Lisper





Re: non-linear patterns

1999-05-05 Thread Bjorn Lisper

Term rewriting theory indicates that non-linear patterns should be treated
with care. For instance, a term rewriting systems with a non-linear rule
will in general not have a normalizing rewrite strategy which is sequential,
and nonlinear term rewriting systems are not orthogonal which means they may
be nonconfluent.

So Haskell's pattern matching is not precisely the same as term rewriting,
but anyway.

Bjorn Lisper





Re: STL Like Library For Haskell

1999-04-29 Thread Bjorn Lisper

Me:
 There is another good reason to have a total order: it makes reduction
 operations (folds) over the structure well-defined.
Kevin Atkinson:
But how important is having a fold well defined.  For many common
numerical operations such as summing a list, taking the product of a
list, etc. The order in which the elements get folded does not matter. 
All that matters is that each element gets represented exactly once.

It depends. Sometimes you do fold over binary operations which are
associative and commutative, and then the order does not matter of
course. But sometimes this is not true - floating-point operations are
typically not exactly associative and commutative and then the ordering does
matter for the numerical precision. It must then be up to the programmer to
use knowledge of the numerical algorithm and indata to decide whether a
"loose specification" of fold which does not require a well-defined ordering
is OK or not.

One can come up with examples where the order of summation makes a big
difference in error propagation. Whether pathological or not, the programmer
must have control when necessary.

Other operations are non-commutative, like function composition. There is an
amusing example in Hillis, Steele "Data Parallel Algorithms", CACM vol 29,
1986, where they derive a data parallel algorithm for lexical analysis by
expressing this problem as a reduction over composition of transition
functions for finite automata. This reduction can be parallelized due to
associativity of function composition but the order of the functions being
composed cannot be changed.

 We require that there is a function enumerate :: Bounds a - [a] which
 provides a total order of the elements 

Do strings fall within the requirements?

Yes (use lexicographic order), but we don't have them for now.

Bjorn Lisper