[Haskell-cafe] develop new Haskell shell?

Brian Hulley brianh at metamilk.com
Fri May 12 09:48:25 EDT 2006


Donn Cave wrote:
> On Thu, 11 May 2006, Brian Hulley wrote:
> ...
>> -- catenate all files in a specified directory
>>
>> catenate outputFile dir = withDir dir $
>>                                             ls >>= cat outputFile
>
> So, you would apply this like
>     catenate "result" "/etc/stuff"  ?  String literals need quotes?

Yes - why not? Also, on Windows for example, filenames can have spaces so 
quotes are needed anyway with any shell at the moment if such filenames are 
used. However if this was a real problem it *might* be possible to relax the 
need for quotes by using a much more complicated parsing algorithm that 
could take into account the types of expected args and coerce the 
appropriate unquoted lexemes/expressions into strings, but I don't know if 
this would really be worth the trouble, and it would introduce ambiguity eg 
is etc/stuff a filename or an arithmetic expression?

>
>> Of course the above could no doubt be improved but surely it is
>> already far easier to understand and much more powerful than the
>> idiosyncratic text
>> based approach used in UNIX shells (including rc).
>
> (cd /etc/stuff; cat * > result)

Well the problem here is that the command leaves you in /etc/stuff so you 
have to remember this when you subsequently execute another command. The 
advantage of withDir is that the original directory is restored afterwards, 
which might make it easier to write modular scripts.
In any case you could also make a cd command in Haskell, and write:

cd "etc/stuff" >> ls >>= cat "result"

>
> ?
>
>> renif extFrom extTo fileName =
>>         case split fileName of
>>              (n, ext) | ext == extFrom -> rename fileName (unsplit
>>              (n, extTo)) _ -> return ()
>>
>> %    ls >>= mapM_ (renif "txt" "hs")
>
> $  for a in *.txt; do mv $a $(basename $a .txt); done

Well someone had to define the meaning of basename so if we make the 
definition of renif similarly built-in the comparison is between

      ls >>= mapM_ (renif "txt" "hs")

and

     for a in *.txt; do mv $a $(basename $a .txt); done

So the Haskell command is shorter, easier to read, and more re-usable, 
because mapM_ (renif "txt" "hs") can be used anywhere that supplies a list 
of files whereas "for a  in *.txt" doesn't make the source of the list 
explicit. Do they come from the current directory? What if some other list 
of files should be used?


>
> ?  Not saying the UNIX shell is a rich and well structured programming
> environment, and maybe FP is a good direction for that problem.  But
> don't underestimate it, the principles behind it are sharp, and while
> I think you could expect to win on complex data structures, you can't
> afford to lose on simple commands, because that's where most of the
> action is.

>From the above even the simple commands are easier in Haskell. The only 
drawback is the need to put quotes round filenames/paths but imho this 
doesn't seem like a major problem compared to the ease with which complex 
commands can be built up and the advantage of only having to learn one 
universal language.

>
> Hm.  Not to pick at the details too much, but you know "cat" is
> actually a standard UNIX command, that writes to standard output
> and has no output file parameter?  What's up with the new parameter
> in your version - was it not going to be workable the way it was?

I forgot about this. You could define cat in Haskell as:

cat :: [FileName] -> Shell String

and have another command analogous to > to write a string into a file, say 
"into"

into :: FileName -> String -> Shell ()

Then you could catenate all files in the current directory into a file 
called "result" by:

ls >>= cat >>= into "result"

(Same as cat * > result)

So in balance I think that while some UNIX commands may be slightly shorter, 
the shortness comes at the expense of the assumptions they have to make 
about the kinds of things you want to do eg cat * works well if the only 
possible source of files is the current directory, but doesn't work at all 
if you want to create a list of files from some other operation (unless you 
create a temporary directory with symlinks etc but it easily degenerates 
into a very complicated mess compared to Haskell).

Regards, Brian. 



More information about the Haskell-Cafe mailing list