Re: preprocessing printf/regex strings (like ocaml)
On Sun, May 12, 2002, Oliver George wrote:
perl like things...
msg' = replace "s/love/lust/" msg
or, nice regex stuff...
main = case match "^(\d+)" of Nothing -> 0 Just (i) -> i
Ick! regexes can be handled much better than that. Imagine something like: q = case x of /"^$" -> "Empty Line." /"^(foo@\d+)" -> foo ++ "hello!" /"confusion (foo@?) (bar@*)" -> "foo is" ++ foo ++ "And bar is" ++bar /"(a@*)" -> error "Sorry: I don't know what to do with "++a I don't know if this sort of syntax could work, but something similar would seem sensible. If it's not clear: the regex is used as a pattern. If it matches the string, that path is taken in the case statement. Also, wherever there are parenthesis with a "variable @", that variable is bound to the relevent string portion.
the python string notation (str % tuple) would fit really well too...
putStrLn "hello %s, you got %d right" % ("oliver", 5)
Might be nice.
Am I the only one who sees this as being a really valuable extension to haskell? or does it exist and i've just never noticed?
cheers, Oliver. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Night. An owl flies o'er rooftops. The moon sheds its soft light upon the trees. David Feuer
the python string notation (str % tuple) would fit really well too... putStrLn "hello %s, you got %d right" % ("oliver", 5)
Might be nice.
What would be the type of putStrLn then?
The type of putStrLn would remain unchanged. The idea would be to let the compiler translate the string "hello %s, you got %d right" into the function (\ (p1, p2) -> "hello " ++ p1 ++ ", you got " ++ show p2 ++ " right") so that the type system can do its work. Then the % above is only an application. There is of course no need for the tuple; a curried function would probably be more convenient. You may ask, how would the compiler know that this "string" is meant to be a function ? I think it would be nice to have a similar syntax for matching strings and for building strings. Following David's example: q = case x of /"^$" -> "Empty Line." /"^(foo@\d+)" -> /"%s hello!" foo /"confusion (foo@?) (bar@*)" -> /"foo is %s And bar is %s" foo bar /"(a@*)" -> error (/"Sorry: I don't know what to do with %s" a) -- "Choose Again."
On Sunday 12 May 2002 03:50 am, Sebastien Carlier wrote:
the python string notation (str % tuple) would fit really well too... putStrLn "hello %s, you got %d right" % ("oliver", 5)
Might be nice.
What would be the type of putStrLn then?
The type of putStrLn would remain unchanged.
The idea would be to let the compiler translate the string "hello %s, you got %d right" into the function (\ (p1, p2) -> "hello " ++ p1 ++ ", you got " ++ show p2 ++ " right") so that the type system can do its work. Then the % above is only an application. There is of course no need for the tuple; a curried function would probably be more convenient.
Here is a printf-style function that I hacked up this morning; it uses type classes but it doesn't need functional dependencies: module Printf where main = putStrLn $ printf "%i * %c = %d %s." (2::Integer) 'c' (6.0e8::Double) "meters/sec" class Printf a where printf :: String -> a printf' :: ShowS -> String -> a printf = printf' id instance Printf String where printf' pre pattern = pre pattern instance (Format a, Printf b) => Printf (a -> b) where printf' pre pattern x = let (text, pat') = break ('%'==) pattern (formatted, rest) = format x pat' in printf' (pre . showString text . showString formatted) rest -------------------------------------------- class Format a where format :: a -> String -> (String, String) instance Format Char where format c pat = case pat of '%':'c':rest -> ([c],rest) _ -> error "printf: extra char argument" instance Format String where format s pat = case pat of '%':'s':rest -> (s,rest) _ -> error "printf: extra string argument" instance Format Integer where format i pat = case pat of '%':'i':rest -> (show i,rest) _ -> error "printf: extra integer argument" instance Format Double where format d pat = case pat of '%':'d':rest -> (show d,rest) _ -> error "printf: extra double argument"
Brian Huffman <bhuffman@galois.com> wrote:
Here is a printf-style function that I hacked up this morning; it uses type classes but it doesn't need functional dependencies: [snip]
It's very nice and even extendable, though `class Printf String' is unfortunately not Haskell 98. But the bigger question is, how to support Posix-style positional arguments? They are essential for i18n. For instance,
printf "%1$s %2$s" "foo" "bar" -- ==> "foo bar" printf "%2$s %1$s" "foo" "bar" -- ==> "bar foo"
Naturally, such format strings cannot be pre-processed by the compiler since they are typically loaded from some message database at run time. -- anatoli t. __________________________________________________ Do You Yahoo!? LAUNCH - Your Yahoo! Music Experience http://launch.yahoo.com
tis 2002-05-14 klockan 06.37 skrev anatoli:
Brian Huffman <bhuffman@galois.com> wrote:
Here is a printf-style function that I hacked up this morning; it uses type
classes but it doesn't need functional dependencies: [snip]
It's very nice and even extendable, though `class Printf String' is unfortunately not Haskell 98. But the bigger question is, how to support Posix-style positional arguments? They are essential for i18n.
For instance,
printf "%1$s %2$s" "foo" "bar" -- ==> "foo bar" printf "%2$s %1$s" "foo" "bar" -- ==> "bar foo"
Naturally, such format strings cannot be pre-processed by the compiler since they are typically loaded from some message database at run time.
I agree that i18n needs positional arguments. What's wrong with simply doing like this: printf "I have %. %. %.." ["trained", show 1, "Jedi"] printf "%2. %3. %1. I have." ["trained", show 1, "Jedi"] with printf would look something like this: printf ('%':'%':rest) xs = '%' : printf rest xs printf ('%':'.':rest) (x:xs) = x ++ printf rest xs printf ('%':d:rest) xs | isDigit d = let (ds, rest') = span isDigit rest index = read (d:ds) in if null rest' || head rest' /= '.' || index > length xs then '%':printf (d:ds:rest') xs else xs!!(index - 1) ++ printf (tail rest') xs printf (r:rest) xs = r:printf rest xs printf [] _ = [] Note that there are no errors if the format string is wrong in any way, it's just unchanged. Also, behaviour with both positional and normal formatters is not considered. Feel free to use this code snippet however you like. Regards, Martin -- Martin Norbäck d95mback@dtek.chalmers.se Kapplandsgatan 40 +46 (0)708 26 33 60 S-414 78 GÖTEBORG http://www.dtek.chalmers.se/~d95mback/ SWEDEN OpenPGP ID: 3FA8580B
Martin Norb�ck <d95mback@dtek.chalmers.se> wrote:
I agree that i18n needs positional arguments. What's wrong with simply doing like this:
printf "I have %. %. %.." ["trained", show 1, "Jedi"] printf "%2. %3. %1. I have." ["trained", show 1, "Jedi"]
Nothing is exceptionally wrong with it, except it's not as flexible. Since everything is show'n, how would you handle things like "%5.2f" or "%*d"? In Brian Huffman's version it's almost trivial to add. I know I can use formatDouble and whatnot, but the code looks cluttered this way. "C" printf has many pitfalls, but I like its terseness. -- anatoli __________________________________________________ Do You Yahoo!? LAUNCH - Your Yahoo! Music Experience http://launch.yahoo.com
Martin Norbäck <d95mback@dtek.chalmers.se> wrote:
I agree that i18n needs positional arguments. What's wrong with simply doing like this:
printf "I have %. %. %.." ["trained", show 1, "Jedi"] printf "%2. %3. %1. I have." ["trained", show 1, "Jedi"]
Nothing is exceptionally wrong with it, except it's not as flexible. Since everything is show'n, how would you handle things like "%5.2f" or "%*d"? In Brian Huffman's version it's almost trivial to add. I know I can use formatDouble and whatnot, but the code looks cluttered this way. "C" printf has many pitfalls, but I like its terseness.
Just thought I would jump in and say that, unlike (it seems) everyone else, I hate printf in C. It is a horrible horrible inextensible hack of a function that I find extremely awkward to use. In the C version, it is completely hardcoded and inextensible. Even in the version presented on this list, one can't add new ways to format an existing datatype. I personally much prefer the syntax currently used in Haskell, which is also essentially what is used in most other recent languages, including Java, C++, and (god help me) Perl. In the example given, I could write: "I have " ++ action ++ " " ++ number ++ " " ++ whatas where action = "trained" number = show 1 whatas = "Jedi" Which is IMHO rather more readable than a load of weird control codes hidden in a text string that one then has to match against a list. + If I want to use a weird formatting approach, I just write my own function, and use it instead of "show". No need to faff around extending someone else's printf. [end rant] -Rob
tis 2002-05-14 klockan 16.45 skrev Robert Ennals:
Martin Norbäck <d95mback@dtek.chalmers.se> wrote:
I agree that i18n needs positional arguments. What's wrong with simply doing like this:
printf "I have %. %. %.." ["trained", show 1, "Jedi"] printf "%2. %3. %1. I have." ["trained", show 1, "Jedi"]
Nothing is exceptionally wrong with it, except it's not as flexible. Since everything is show'n, how would you handle things like "%5.2f" or "%*d"? In Brian Huffman's version it's almost trivial to add. I know I can use formatDouble and whatnot, but the code looks cluttered this way. "C" printf has many pitfalls, but I like its terseness.
Changing format specifiers normally doesn't happen during translation. Word order changes happen.
I personally much prefer the syntax currently used in Haskell, which is also essentially what is used in most other recent languages, including Java, C++, and (god help me) Perl.
In the example given, I could write:
"I have " ++ action ++ " " ++ number ++ " " ++ whatas where action = "trained" number = show 1 whatas = "Jedi"
How do you internationalize this code snippet? The issue here was with i18n. When doing i18n, you need to give the translator the possibility to change the word order, hence the Yoda example.
Which is IMHO rather more readable than a load of weird control codes hidden in a text string that one then has to match against a list.
The point with hiding them in a control string is that you can have the translator translate the control string, and not have to change the source code, like with gettext. Very nice system. Regards, Martin -- Martin Norbäck d95mback@dtek.chalmers.se Kapplandsgatan 40 +46 (0)708 26 33 60 S-414 78 GÖTEBORG http://www.dtek.chalmers.se/~d95mback/ SWEDEN OpenPGP ID: 3FA8580B
On Tue, May 14, 2002 at 03:45:36PM +0100, Robert Ennals wrote:
Just thought I would jump in and say that, unlike (it seems) everyone else, I hate printf in C. It is a horrible horrible inextensible hack of a function that I find extremely awkward to use. ... I personally much prefer the syntax currently used in Haskell, which is also essentially what is used in most other recent languages, including Java, C++, and (god help me) Perl.
In the example given, I could write:
"I have " ++ action ++ " " ++ number ++ " " ++ whatas where action = "trained" number = show 1 whatas = "Jedi"
Which is IMHO rather more readable than a load of weird control codes hidden in a text string that one then has to match against a list.
How would you deal with internationalisation issues? --Dylan
Robert Ennals <Robert.Ennals@cl.cam.ac.uk> wrote:
I personally much prefer the syntax currently used in Haskell, which is also essentially what is used in most other recent languages, including Java, C++, and (god help me) Perl.
In the example given, I could write:
"I have " ++ action ++ " " ++ number ++ " " ++ whatas where action = "trained" number = show 1 whatas = "Jedi"
This is all fine and dandy, but how would you translate this to 42 different languages your customers want supported, with different word order and all that? -- anatoli t. __________________________________________________ Do You Yahoo!? LAUNCH - Your Yahoo! Music Experience http://launch.yahoo.com
Robert Ennals <Robert.Ennals@cl.cam.ac.uk> wrote:
I personally much prefer the syntax currently used in Haskell, which is also essentially what is used in most other recent languages, including Java, C++, and (god help me) Perl.
In the example given, I could write:
"I have " ++ action ++ " " ++ number ++ " " ++ whatas where action = "trained" number = show 1 whatas = "Jedi"
This is all fine and dandy, but how would you translate this to 42 different languages your customers want supported, with different word order and all that?
Surely that problem only arises if one insists on encoding all the relevant information inside a string. An alternative would be to encode all user-visible messages in an external module, with a Haskell function for each message. The translator would then redefine this module for each language. It doesn't involve any more complexity - it just shifts the complexity into a more expressive language. For example: module Messages -- English language version where stuffDone :: String -> Int -> String -> String stuffDone action number whatas = "I have " ++ action ++ " " ++ (show number) ++ " " ++ whatas jedi = "Jedi" trained = "Trained" Normal code then does the following: import qualified Messages as M putStrLn $ M.stuffDone M.trained 1 M.jedi Much nicer IMHO. -Rob
Robert Ennals <Robert.Ennals@cl.cam.ac.uk> wrote:
Surely that problem only arises if one insists on encoding all the relevant information inside a string.
This is pretty much the only option, because translators and programmers are different people. Translators can deal with simple text files with one message string per line and not much else. You can't hire a translation firm and tell them "translate this Haskell module for me". You can treat message strings as declarations in a specialised language. This language can be typed, and you could theoretically typecheck it against your Haskell program using specialised tools. But translators need to see simple readable message strings. -- anatoli t. __________________________________________________ Do You Yahoo!? LAUNCH - Your Yahoo! Music Experience http://launch.yahoo.com
tis 2002-05-14 klockan 18.56 skrev anatoli:
Robert Ennals <Robert.Ennals@cl.cam.ac.uk> wrote:
Surely that problem only arises if one insists on encoding all the relevant information inside a string.
This is pretty much the only option, because translators and programmers are different people. Translators can deal with simple text files with one message string per line and not much else. You can't hire a translation firm and tell them "translate this Haskell module for me".
You can treat message strings as declarations in a specialised language. This language can be typed, and you could theoretically typecheck it against your Haskell program using specialised tools. But translators need to see simple readable message strings.
I played around with this some more. You can see some files at http://www.dtek.chalmers.se/~d95mback/gettext/ if you are interested. The use of unsafePerformIO may be unsafe here, I really haven't got a clue. When in doubt, use i18n instead of __ :) Output examples: martin@lexie:~/gettext$ LANG=sv_SE ./Main Hej världen! 1 Jedi trained I have. martin@lexie:~/gettext$ LANG=en_US ./Main Hello, world! I have trained 1 Jedi. Regards, Martin
Robert Ennals <Robert.Ennals@cl.cam.ac.uk> wrote:
Surely that problem only arises if one insists on encoding all the relevant information inside a string.
This is pretty much the only option, because translators and programmers are different people. Translators can deal with simple text files with one message string per line and not much else. You can't hire a translation firm and tell them "translate this Haskell module for me".
You can treat message strings as declarations in a specialised language. This language can be typed, and you could theoretically typecheck it against your Haskell program using specialised tools. But translators need to see simple readable message strings.
I don't really see what makes a string such as "I have %. %. %.." [where the user has to work out what the substrings are] any harder to deal with than "I have " ++ action ++ " " ++ number ++ " " ++ whatas other from the fact that the former is what C does. A translator doesn't need to know Haskell. They just need to know that, when, in the messages module they see englishword = "some string" they put the translation of the word into the string. And if they see a message like msgname part otherpart = "string " ++ part ++ " string" ++ otherpart They change the strings, and reorder the parts to make it a sensible sentence in the target language. AFAICS the only reason to use printf strings is because that is what some people are used to, not because it is sensible system to be using. i18n is a useful hack to retrofit onto the C printf system, but I think it would be a backward step for Haskell. -Rob
tor 2002-05-16 klockan 10.50 skrev Robert Ennals:
I don't really see what makes a string such as
"I have %. %. %.." [where the user has to work out what the substrings are]
any harder to deal with than
"I have " ++ action ++ " " ++ number ++ " " ++ whatas
other from the fact that the former is what C does.
Because in one case you just need to change the string, which is in a database separate from the program, in a standard format (po). We could have some other syntaxes which makes things clearer, like printf_named "I have %action; %number; %whatas;." [("action","trained"),("number", show 1), ("whatas", "Jedi")] In the other case you need to change the program, and recompile. So in the second case you need one compiled program for every language you support. If your volonteer translators have to compile the program as well, you might not get any translations at all due to the high barrier compiling is.
AFAICS the only reason to use printf strings is because that is what some people are used to, not because it is sensible system to be using.
I have experience with different systems for translation. If you translate a number of programs (like I've done), you come to appreciate that they use the same system. Gettext is the by far most used system, and it has a lot of nice properties, like the ability to translate an application without having to recompile it and good tool support.
i18n is a useful hack to retrofit onto the C printf system, but I think it would be a backward step for Haskell.
Since I've never seen any i18n systems for Haskell, everything would be a step forward. Regards, Martin -- Martin Norbäck d95mback@dtek.chalmers.se Kapplandsgatan 40 +46 (0)708 26 33 60 S-414 78 GÖTEBORG http://www.dtek.chalmers.se/~d95mback/ SWEDEN OpenPGP ID: 3FA8580B
I wrote a printflike function which used existential types and a pretty simple class a while ago, although in retrospect i could have done it better with partial application (and pure haskell 98). http://homer.netmar.com/~john/computer/haskell/Format.hs John -- --------------------------------------------------------------------------- John Meacham - California Institute of Technology, Alum. - john@foo.net ---------------------------------------------------------------------------
On Tuesday, May 14, 2002, at 06:37 AM, anatoli wrote:
Brian Huffman <bhuffman@galois.com> wrote:
Here is a printf-style function that I hacked up this morning; it uses type classes but it doesn't need functional dependencies: [snip]
It's very nice and even extendable, though `class Printf String' is unfortunately not Haskell 98.
I agree that it is a very nice use of type classes. But all type checking is done at runtime, because the code which is generated depends not on the string itself, but on the types of the arguments which are applied to (printf <<format string>>). For example, putStrLn $ printf "%s" (1 :: Integer) gives no error at compilation, but fails at runtime with: Program error: printf: extra integer argument
But the bigger question is, how to support Posix-style positional arguments? They are essential for i18n.
I hacked Brian's code to add this feature, see the attachment.
For instance,
printf "%1$s %2$s" "foo" "bar" -- ==> "foo bar" printf "%2$s %1$s" "foo" "bar" -- ==> "bar foo"
Naturally, such format strings cannot be pre-processed by the compiler since they are typically loaded from some message database at run time.
Then you give up static type checking for format strings... Why not let the compiler pre-process this database, and generate some type-safe dynamically loadable object ? Or, you could embed a very restricted version of the compiler in the program, to pre-process and type-check the format strings at runtime (Yes, you would need to keep some type information in the executable program). -- Sébastien
Jorge Adriano <jadrian@mat.uc.pt> writes:
the python string notation (str % tuple) would fit really well too... putStrLn "hello %s, you got %d right" % ("oliver", 5)
Might be nice.
What would be the type of putStrLn then?
some solutions to this: - cayenne http://www.cs.chalmers.se/~augustss/cayenne/ - ocaml's printf (special typing done by the compiler) http://caml.inria.fr/oreilly-book/html/book-ora076.html#toc105 - ocaml's printf could also be achieved via camlp4 (?) - you can also give up the sugar and write it (irk!): format (int oo lit " is " oo str oo eol) instead of sprintf "%d is %s\n" see "Functional Unparsing" http://www.brics.dk/RS/98/12/ http://tkb.mpl.com/~tkb/software.html
participants (10)
-
anatoli -
Brian Huffman -
David Feuer -
Dylan Thurston -
John Meacham -
Jorge Adriano -
Martin Norbäck -
Pixel -
Robert Ennals -
Sebastien Carlier