caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments?
@ 2013-11-25 15:46 Matej Kosik
  2013-11-25 16:27 ` Gabriel Scherer
  2013-11-27 11:54 ` [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments? Jeremie Dimino
  0 siblings, 2 replies; 10+ messages in thread
From: Matej Kosik @ 2013-11-25 15:46 UTC (permalink / raw)
  To: caml-list

Dear all,

I would like to define custom directives, that would enable me to write code like e.g. this:

Print.printf "regular %bold_on bold %bold_off regular %italic_on italic %italic_off";

This might expand to (in HTML codes)

              regular <B>bold</B> regular <I>italic</I>

or in ANSI codes to something analogous.

I am currently looking at:

  http://ocaml-batteries-team.github.io/batteries-included/hdoc/BatPrint.html

It refers to:

  https://github.com/ocaml-batteries-team/batteries-included/blob/master/examples/snippets/test_printf.ml

Those things work, although there is no example for:

  Print.literal

which might be (?) what I need to employ.

Can somebody give me some advice how to create simple "parameterless format directives" (like those above)?

----

Basically, I just want to refactor some weird markup out of the literal string while I do not want to reinsert the refactored stuff via %s because it is not maintainable.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments?
  2013-11-25 15:46 [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments? Matej Kosik
@ 2013-11-25 16:27 ` Gabriel Scherer
  2013-11-27 11:37   ` Matej Kosik
  2013-11-27 11:54 ` [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments? Jeremie Dimino
  1 sibling, 1 reply; 10+ messages in thread
From: Gabriel Scherer @ 2013-11-25 16:27 UTC (permalink / raw)
  To: Matej Kosik; +Cc: caml-list

Adding custom printing directives requires a syntax extension to
rewrite those directives into OCaml code. The built-in support for
magicly-typed formats in the OCaml language is not extensible (it is
complex enough as it is).
Batteries 1 used to provide such a syntax extension, but we
(re)discovered that users don't like syntax extensions: they make code
harder to compile/deploy, are controversial, and in the long run often
make our life harder instead of easier. Batteries 2 does not come with
any syntax extension, and I think we're better off as is (of course
you're free to add your own in your code).

Of course, such a natural idea is bound to be reused, and one of the
first libraries released by Jérémie Dimino at Jane Street was
precisely such a custom-printf camlp4 extension:
  https://github.com/janestreet/custom_printf

Feel free to reuse it or extend it for any project where it makes
sense. In particular, I don't think that using it implies any
dependency of your preprocessed code on Core, though of course you'll
need Sexplib to use the format-to-sexplib feature. My purely personal
advice would be to seriously explore non-syntax-extension approaches
(combinator library?) before making such a step.

On Mon, Nov 25, 2013 at 4:46 PM, Matej Kosik
<5764c029b688c1c0d24a2e97cd764f@gmail.com> wrote:
> Dear all,
>
> I would like to define custom directives, that would enable me to write code like e.g. this:
>
> Print.printf "regular %bold_on bold %bold_off regular %italic_on italic %italic_off";
>
> This might expand to (in HTML codes)
>
>               regular <B>bold</B> regular <I>italic</I>
>
> or in ANSI codes to something analogous.
>
> I am currently looking at:
>
>   http://ocaml-batteries-team.github.io/batteries-included/hdoc/BatPrint.html
>
> It refers to:
>
>   https://github.com/ocaml-batteries-team/batteries-included/blob/master/examples/snippets/test_printf.ml
>
> Those things work, although there is no example for:
>
>   Print.literal
>
> which might be (?) what I need to employ.
>
> Can somebody give me some advice how to create simple "parameterless format directives" (like those above)?
>
> ----
>
> Basically, I just want to refactor some weird markup out of the literal string while I do not want to reinsert the refactored stuff via %s because it is not maintainable.
>
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments?
  2013-11-25 16:27 ` Gabriel Scherer
@ 2013-11-27 11:37   ` Matej Kosik
  2013-11-28  6:30     ` [Caml-list] [batteries] ... how to create (format) directives oleg
  0 siblings, 1 reply; 10+ messages in thread
From: Matej Kosik @ 2013-11-27 11:37 UTC (permalink / raw)
  To: Gabriel Scherer; +Cc: caml-list

On 25/11/13 16:27, Gabriel Scherer wrote:
> Adding custom printing directives requires a syntax extension to
> rewrite those directives into OCaml code. The built-in support for
> magicly-typed formats in the OCaml language is not extensible (it is
> complex enough as it is).
> Batteries 1 used to provide such a syntax extension, but we
> (re)discovered that users don't like syntax extensions: they make code
> harder to compile/deploy, are controversial, and in the long run often
> make our life harder instead of easier. Batteries 2 does not come with
> any syntax extension, and I think we're better off as is (of course
> you're free to add your own in your code).
> 
> Of course, such a natural idea is bound to be reused, and one of the
> first libraries released by Jérémie Dimino at Jane Street was
> precisely such a custom-printf camlp4 extension:
>   https://github.com/janestreet/custom_printf
> 
> Feel free to reuse it or extend it for any project where it makes
> sense. In particular, I don't think that using it implies any
> dependency of your preprocessed code on Core, though of course you'll
> need Sexplib to use the format-to-sexplib feature. My purely personal
> advice would be to seriously explore non-syntax-extension approaches
> (combinator library?) before making such a step.

I may, unfortunatelly, need some more pointers to understand this suggestion.

The problematic code looks somehow like this:


        (* The following string is much longer than what is inlined here. *)

        bold_on ^ "mdx" ^ bold_off ^ " " ^ underline_on ^ "command" ^ underline_off ^ "
        Perform a given " ^ underline_on ^ "command" ^ underline_off ^ ".
        Section COMMANDS describes all the supported commands."

where:

	bold_on
	bold_off
	underline_on
	underline_off

are some strings.

It works, but it is ugly.

That is why I asked about possibilities to make it more decent.

Since Print module is available, I thought that that may be one of the ways how to achieve that.
If only I were able to define (non-parametric control strings) that I could use in the following way:

	"%Bmdx%B %Icommand%I

	 Perform a given %Icommand%I.

         Section COMMANDS describes all the supported commands."

This looks, sort of, OK.

I believe that the Print module in batteries 1.4 might enable me to do just that although I need some examples.
I do not care too much that given feature disappeared in Batteries 2.0
(Batteries 2.0 is not yet available in Debian stable/testing/unstable
 and when in descends there, that time might be beyond the lifecycle of the program I am writing.
 If not, I will bother about it at right time. There might even be more solutions than there are now.).

If you mean that it is not technically possible to do what I thought was possible,
then I am not against "combinator library", although I am not sure, what do you mean.

I would be grateful if you (or somebody) sent a few links to things that might be regarded as combinator libraries
and perhaps, if given combinator library existed, that would solve my problem,
how it would make my_life_easier & my_code_more_compact than the (somewhat verbose) approach I am currently using
(string concatenation).

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments?
  2013-11-25 15:46 [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments? Matej Kosik
  2013-11-25 16:27 ` Gabriel Scherer
@ 2013-11-27 11:54 ` Jeremie Dimino
  2013-11-27 12:52   ` Matej Kosik
  1 sibling, 1 reply; 10+ messages in thread
From: Jeremie Dimino @ 2013-11-27 11:54 UTC (permalink / raw)
  To: Matej Kosik; +Cc: caml-list

[-- Attachment #1: Type: text/plain, Size: 553 bytes --]

On Mon, Nov 25, 2013 at 3:46 PM, Matej Kosik <
5764c029b688c1c0d24a2e97cd764f@gmail.com> wrote:

> I would like to define custom directives, that would enable me to write
> code like e.g. this:
>
> Print.printf "regular %bold_on bold %bold_off regular %italic_on italic
> %italic_off";

[...]
> Can somebody give me some advice how to create simple "parameterless
> format directives" (like those above)?
>

IIRC, this should work:

   let printer_bold_on k = k (fun oc -> output_string oc "blah")

to define the format directive %bold_on.

-- 
Jeremie

[-- Attachment #2: Type: text/html, Size: 1173 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments?
  2013-11-27 11:54 ` [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments? Jeremie Dimino
@ 2013-11-27 12:52   ` Matej Kosik
  2013-11-27 13:00     ` Jeremie Dimino
  0 siblings, 1 reply; 10+ messages in thread
From: Matej Kosik @ 2013-11-27 12:52 UTC (permalink / raw)
  To: Jeremie Dimino; +Cc: caml-list

On 27/11/13 11:54, Jeremie Dimino wrote:
> On Mon, Nov 25, 2013 at 3:46 PM, Matej Kosik <5764c029b688c1c0d24a2e97cd764f@gmail.com <mailto:5764c029b688c1c0d24a2e97cd764f@gmail.com>> wrote:
> 
>     I would like to define custom directives, that would enable me to write code like e.g. this:
> 
>     Print.printf "regular %bold_on bold %bold_off regular %italic_on italic %italic_off"; 
> 
>     [...]
>     Can somebody give me some advice how to create simple "parameterless format directives" (like those above)?
> 
> 
> IIRC, this should work:
> 
>    let printer_bold_on k = k (fun oc -> output_string oc "blah")
> 
> to define the format directive %bold_on.


Thank you. This was what I was looking for.
Now I can define something like this:

        let bold_is_open = ref false
        let printer_B k = k (fun oc -> if !bold_is_open then
                                         begin
                                           output_string oc ANSI.bold_off;
                                           bold_is_open := false
                                         end
                                       else
                                         begin
                                           output_string oc ANSI.bold_on;
                                           bold_is_open := true
                                         end
                               )

and then:

	Print.printf p"foo %B%!bar%B baz"

instead of

	print_string ("foo " ^ bold_on ^ "bar" ^ bold_off ^ "baz")

So this is a progress.
The only thing that adds friction is that I cannot leave out

	%!

because if I did it, I would get
a complain about unbound "print_Bbar" variable.

Thus, where I non-identifier-character follows %B, I must put extra %! instruction.
This is not too bad although still one step from ideal.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments?
  2013-11-27 12:52   ` Matej Kosik
@ 2013-11-27 13:00     ` Jeremie Dimino
  2013-11-29  9:32       ` Matej Kosik
  0 siblings, 1 reply; 10+ messages in thread
From: Jeremie Dimino @ 2013-11-27 13:00 UTC (permalink / raw)
  To: Matej Kosik; +Cc: caml-list

[-- Attachment #1: Type: text/plain, Size: 285 bytes --]

On Wed, Nov 27, 2013 at 12:52 PM, Matej Kosik <
5764c029b688c1c0d24a2e97cd764f@gmail.com> wrote:

> Thus, where I non-identifier-character follows %B, I must put extra %!
> instruction.
> This is not too bad although still one step from ideal.
>

You can also write %(B).

-- 
Jeremie

[-- Attachment #2: Type: text/html, Size: 628 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives
  2013-11-27 11:37   ` Matej Kosik
@ 2013-11-28  6:30     ` oleg
  2013-11-29  9:32       ` Matej Kosik
  0 siblings, 1 reply; 10+ messages in thread
From: oleg @ 2013-11-28  6:30 UTC (permalink / raw)
  To: 5764c029b688c1c0d24a2e97cd764f; +Cc: caml-list, gabriel.scherer


Matej Kosik wrote:
> I would be grateful if you (or somebody) sent a few links to things
> that might be regarded as combinator libraries and perhaps, if given
> combinator library existed, that would solve my problem, how it would
> make my_life_easier & my_code_more_compact than the (somewhat verbose)
> approach I am currently using (string concatenation).

Enclosed is one example of a combinator library for formatting, in
plain OCaml (even Caml-lite, probably), with no extensions, GADTs,
type classes, etc. Here is a simple demo

let ex1 = let open PrintComp in 
  pr s"1" s"2" printf

(* prints: 12 *)

(* Look!  No string concatenation operations! We separate operations
  with mere white space (and often even it is not needed
*)

let ex2 = let open PrintComp in 
  pr s"1" s"2" b"3" i 4 
  sprintf
(*
    val ex2 : string = "12<bold>3</bold>4"
*)


(* The format is really typed *)
let ex3 = let open PrintComp in 
  pr s"1" s"2" i "x" b"3" i 4 
  sprintf
(*
    Characters 50-53:
    pr s"1" s"2" i "x" b"3" i 4 
                   ^^^
Error: This expression has type string but an expression was expected of type
         int
*)


(* It is possible to avoid s" " below, so to insert interworld space
   automatically
*)

let ex4 = let open PrintComp in pr
  b"mdx" s" " it "command" br
  s"Perform a given " it "command" br
  s"Section COMMANDS describes all the supported commands."
  sprintf

(*
val ex4 : string =
  "<bold>mdx</bold> <i>command</i>\n\nPerform a given <i>command</i>\n\nSection COMMANDS describes all the supported commands."
*)

(* The formatting sequence can be interrupted, e.g.,
   to bind some common subexpressions or to perform some computations
*)

let ra = fun x f -> f x

let ex5 = let open PrintComp in pr
  b"mdx" s" " 
  (* interrupt the flow *)
  begin let cmd st = ra st it "command" in 
  fun st -> ra st (* continue with the flow *)
  cmd br
  s"Perform a given " cmd br
  s"Section COMMANDS describes all the supported commands." end
  sprintf

(*
val ex5 : string =
  "<bold>mdx</bold> <i>command</i>\n\nPerform a given <i>command</i>\n\nSection COMMANDS describes all the supported commands."
*)


Here is the implementation, of a FORTH like language for formatting.
Polyvariadic functions are possible even in plain OCaml. 

module PrintComp = struct
  (* Put this at the beginning *)
  let pr k = k []

  (* to format a string *)
  let s = fun st (str:string) k -> k (str :: st)

  (* to format a string in bold *)
  let b = fun st (str:string) k -> k ("</bold>" :: str :: "<bold>" :: st)

  (* to format a string in italics *)
  let it = fun st (str:string) k -> k ("</i>" :: str :: "<i>" :: st)

  (* to format an integer *)
  let i = fun st n k -> k (string_of_int n :: st)

  (* generate a line break *)
  let br = fun st k -> k ("\n\n" :: st)

  (* To finally print as a string *)
  let sprintf st = String.concat "" (List.rev st)

  (* To finally print on stdout *)
  let printf st = List.iter print_string (List.rev st)

  (* To finally print on channel *)
  (* similarly *)
end;;



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives
  2013-11-28  6:30     ` [Caml-list] [batteries] ... how to create (format) directives oleg
@ 2013-11-29  9:32       ` Matej Kosik
  2013-11-30  3:15         ` oleg
  0 siblings, 1 reply; 10+ messages in thread
From: Matej Kosik @ 2013-11-29  9:32 UTC (permalink / raw)
  To: oleg; +Cc: caml-list, gabriel.scherer

On 28/11/13 06:30, oleg@okmij.org wrote:
> 
> Enclosed is one example of a combinator library for formatting, in
> plain OCaml (even Caml-lite, probably), with no extensions, GADTs,
> type classes, etc. Here is a simple demo

This is interesting. Thank you.

> 
> let ex1 = let open PrintComp in 
>   pr s"1" s"2" printf
> 
> (* prints: 12 *)
> 
> (* Look!  No string concatenation operations! We separate operations
>   with mere white space (and often even it is not needed
> *)
> 
> let ex2 = let open PrintComp in 
>   pr s"1" s"2" b"3" i 4 
>   sprintf
> (*
>     val ex2 : string = "12<bold>3</bold>4"
> *)
> 
> 
> (* The format is really typed *)
> let ex3 = let open PrintComp in 
>   pr s"1" s"2" i "x" b"3" i 4 
>   sprintf
> (*
>     Characters 50-53:
>     pr s"1" s"2" i "x" b"3" i 4 
>                    ^^^
> Error: This expression has type string but an expression was expected of type
>          int
> *)
> 
> 
> (* It is possible to avoid s" " below, so to insert interworld space
>    automatically
> *)

I do not understand what do you have in mind (above).
(Sometimes spaces are desired, sometimes they are undesired.
 I do not see a general rule that could be embedded to `b' so that it could reliably decide in either way.)

> 
> let ex4 = let open PrintComp in pr
>   b"mdx" s" " it "command" br
>   s"Perform a given " it "command" br
>   s"Section COMMANDS describes all the supported commands."
>   sprintf
> 
> (*
> val ex4 : string =
>   "<bold>mdx</bold> <i>command</i>\n\nPerform a given <i>command</i>\n\nSection COMMANDS describes all the supported commands."
> *)
> 
> (* The formatting sequence can be interrupted, e.g.,
>    to bind some common subexpressions or to perform some computations
> *)
> 
> let ra = fun x f -> f x
> 
> let ex5 = let open PrintComp in pr
>   b"mdx" s" " 
>   (* interrupt the flow *)
>   begin let cmd st = ra st it "command" in 
>   fun st -> ra st (* continue with the flow *)
>   cmd br
>   s"Perform a given " cmd br
>   s"Section COMMANDS describes all the supported commands." end
>   sprintf
> 
> (*
> val ex5 : string =
>   "<bold>mdx</bold> <i>command</i>\n\nPerform a given <i>command</i>\n\nSection COMMANDS describes all the supported commands."
> *)
> 
> 
> Here is the implementation, of a FORTH like language for formatting.
> Polyvariadic functions are possible even in plain OCaml. 
> 
> module PrintComp = struct
>   (* Put this at the beginning *)
>   let pr k = k []
> 
>   (* to format a string *)
>   let s = fun st (str:string) k -> k (str :: st)
> 
>   (* to format a string in bold *)
>   let b = fun st (str:string) k -> k ("</bold>" :: str :: "<bold>" :: st)
> 
>   (* to format a string in italics *)
>   let it = fun st (str:string) k -> k ("</i>" :: str :: "<i>" :: st)
> 
>   (* to format an integer *)
>   let i = fun st n k -> k (string_of_int n :: st)
> 
>   (* generate a line break *)
>   let br = fun st k -> k ("\n\n" :: st)
> 
>   (* To finally print as a string *)
>   let sprintf st = String.concat "" (List.rev st)
> 
>   (* To finally print on stdout *)
>   let printf st = List.iter print_string (List.rev st)
> 
>   (* To finally print on channel *)
>   (* similarly *)
> end;;
> 
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments?
  2013-11-27 13:00     ` Jeremie Dimino
@ 2013-11-29  9:32       ` Matej Kosik
  0 siblings, 0 replies; 10+ messages in thread
From: Matej Kosik @ 2013-11-29  9:32 UTC (permalink / raw)
  To: Jeremie Dimino; +Cc: caml-list

On 27/11/13 13:00, Jeremie Dimino wrote:
> On Wed, Nov 27, 2013 at 12:52 PM, Matej Kosik <5764c029b688c1c0d24a2e97cd764f@gmail.com <mailto:5764c029b688c1c0d24a2e97cd764f@gmail.com>> wrote:
> 
>     Thus, where I non-identifier-character follows %B, I must put extra %! instruction.
>     This is not too bad although still one step from ideal.
> 
> 
> You can also write %(B).

Thanks. That worked well.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] [batteries] ... how to create (format) directives
  2013-11-29  9:32       ` Matej Kosik
@ 2013-11-30  3:15         ` oleg
  0 siblings, 0 replies; 10+ messages in thread
From: oleg @ 2013-11-30  3:15 UTC (permalink / raw)
  To: 5764c029b688c1c0d24a2e97cd764f; +Cc: caml-list, gabriel.scherer


> > (* It is possible to avoid s" " below, so to insert interworld space
> >    automatically
> > *)
>
> I do not understand what do you have in mind (above).  (Sometimes
> spaces are desired, sometimes they are undesired.  I do not see a
> general rule that could be embedded to `b' so that it could reliably
> decide in either way.)
>
> > 
> > let ex4 = let open PrintComp in pr
> >   b"mdx" s" " it "command" br
> >   s"Perform a given " it "command" br
> >   s"Section COMMANDS describes all the supported commands."
> >   sprintf

I think there is a rule, but it depends on the context. For example,
in
        br b"xxx"
the 'b' command should not insert the leading white space, but in
        s"yyy" b"xxx"
it has to. It is not that difficult to implement such a
context-sensitive rule since a formatting instruction like 'b' and
's' has access its the context: that's the 'st' parameter being passed
around. So, a formatting command could check what has occurred before
and insert a whitespace as needed. If we do mean that two strings
should be typeset without an intervening space, we should define a
special formatting instruction 'nosp'.

I forgot to mention that the FORTH-like formatter in the previous
message looks quite similar to the one used in BibTeX. 


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2013-11-30  3:15 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-25 15:46 [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments? Matej Kosik
2013-11-25 16:27 ` Gabriel Scherer
2013-11-27 11:37   ` Matej Kosik
2013-11-28  6:30     ` [Caml-list] [batteries] ... how to create (format) directives oleg
2013-11-29  9:32       ` Matej Kosik
2013-11-30  3:15         ` oleg
2013-11-27 11:54 ` [Caml-list] [batteries] ... how to create (format) directives that do not take any arguments? Jeremie Dimino
2013-11-27 12:52   ` Matej Kosik
2013-11-27 13:00     ` Jeremie Dimino
2013-11-29  9:32       ` Matej Kosik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).