From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail2-relais-roc.national.inria.fr (mail2-relais-roc.national.inria.fr [192.134.164.83]) by sympa.inria.fr (Postfix) with ESMTPS id EE1A77EE6B for ; Thu, 28 Nov 2013 07:30:46 +0100 (CET) Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of oleg@okmij.org) identity=pra; client-ip=66.39.3.115; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="oleg@okmij.org"; x-sender="oleg@okmij.org"; x-conformance=sidf_compatible Received-SPF: Pass (mail2-smtp-roc.national.inria.fr: domain of oleg@okmij.org designates 66.39.3.115 as permitted sender) identity=mailfrom; client-ip=66.39.3.115; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="oleg@okmij.org"; x-sender="oleg@okmij.org"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of postmaster@www1.g3.pair.com) identity=helo; client-ip=66.39.3.115; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="oleg@okmij.org"; x-sender="postmaster@www1.g3.pair.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AgAEAMbhllJCJwNzY2dsb2JhbABZvFSBNQMYBw4IPIIlAQEFJ1IbGBxpFIgGv38Xjh9oBxaEHQOYEwGVPDw X-IPAS-Result: AgAEAMbhllJCJwNzY2dsb2JhbABZvFSBNQMYBw4IPIIlAQEFJ1IbGBxpFIgGv38Xjh9oBxaEHQOYEwGVPDw X-IronPort-AV: E=Sophos;i="4.93,789,1378850400"; d="scan'208";a="45819850" Received: from www1.g3.pair.com ([66.39.3.115]) by mail2-smtp-roc.national.inria.fr with SMTP; 28 Nov 2013 07:30:45 +0100 Received: (qmail 55660 invoked by uid 9370); 28 Nov 2013 06:30:44 -0000 Date: 28 Nov 2013 06:30:44 -0000 Message-ID: <20131128063044.55659.qmail@www1.g3.pair.com> From: oleg@okmij.org To: 5764c029b688c1c0d24a2e97cd764f@gmail.com CC: caml-list@inria.fr CC: gabriel.scherer@gmail.com In-reply-to: <5295D970.8090601@gmail.com> Subject: Re: [Caml-list] [batteries] ... how to create (format) directives 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 = "1234" *) (* 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 = "mdx command\n\nPerform a given command\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 = "mdx command\n\nPerform a given command\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 ("" :: str :: "" :: st) (* to format a string in italics *) let it = fun st (str:string) k -> k ("" :: str :: "" :: 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;;