-- Haskell98!-- | The final view to the typed sprintf and sscanf---- <http://okmij.org/ftp/typed-formatting/FPrintScan.html>---- This code defines a simple domain-specific language of string-- patterns and demonstrates two interpreters of the language:-- for building strings (sprintf) and parsing strings (sscanf).-- This code thus solves the problem of typed printf/scanf sharing the-- same format string posed by Chung-chieh Shan.-- This code presents scanf/printf interpreters in the final style;-- it is thus the dual of the code in PrintScan.hs---- Version: The current version is 1.1, Sep 2, 2008.---- References---- * The complete Haskell98 code with many examples.-- <http://okmij.org/ftp/typed-formatting/PrintScanF.hs>---- * The final view on typed sprintf and sscanf-- <http://okmij.org/ftp/typed-formatting/PrintScanF.txt>---- * The message posted on the Haskell mailing list on Tue, 2 Sep 2008 00:57:18 -0700 (PDT)--moduleText.PrintScanFwhereimportPreludehiding((^))classFormattingSpecreprwherelit::String->repraaint::repra(Int->a)char::repra(Char->a)fpp::PrinterParserb->repra(b->a)(^)::reprbc->reprab->repracinfixl5^-- Printer/parsers (injection/projection pairs)dataPrinterParsera=PrinterParser(a->String)(String->Maybe(a,String))fmt::(FormattingSpecrepr,Showb,Readb)=>b->repra(b->a)fmtx=fppshowread-- The interpreter for printf -- It implements Asai's accumulator-less alternative to-- Danvy's functional unparsingnewtypeFPrab=FPr((String->a)->b)instanceFormattingSpecFPrwherelitstr=FPr$\k->kstrint=FPr$\k->\x->k(showx)char=FPr$\k->\x->k[x]fpp(PrinterParserpr_)=FPr$\k->\x->k(prx)(FPra)^(FPrb)=FPr$\k->a(\sa->b(\sb->k(sa++sb)))-- The interpreter for scanfnewtypeFScab=FSc(String->b->Maybe(a,String))instanceFormattingSpecFScwherelitstr=FSc$\inpx->maybeNothing(\inp'->Just(x,inp'))$prefixstrinpchar=FSc$\inpf->caseinpof(c:inp)->Just(fc,inp)""->Nothingfpp(PrinterParser_pa)=FSc$\inpf->maybeNothing(\(v,s)->Just(fv,s))$painpint=fppshowread(FSca)^(FScb)=FSc$\inpf->maybeNothing(\(vb,inp')->binp'vb)$ainpfsprintf::FPrStringb->bsprintf(FPrfmt)=fmtidsscanf::String->FScab->b->Maybeasscanfinp(FScfmt)f=maybeNothing(Just.fst)$fmtinpf-- One can easily build another interpreter, to convert a formatting-- specification to a C-style formatting string-- Teststp1=sprintf$lit"Hello world"-- "Hello world"ts1=sscanf"Hello world"(lit"Hello world")()-- Just ()tp2=sprintf(lit"Hello "^lit"world"^char)'!'-- "Hello world!"ts2=sscanf"Hello world!"(lit"Hello "^lit"world"^char)id-- Just '!'-- Unit is to keep the type of fmt3 polymorphic and away from the-- monomorphism restrictionfmt3()=lit"The value of "^char^lit" is "^inttp3=sprintf(fmt3())'x'3-- "The value of x is 3"ts3=sscanf"The value of x is 3"(fmt3())(\ci->(c,i))-- Just ('x',3)tp4=sprintf(lit"abc"^int^lit"cde")5-- "abc5cde"ts4=sscanf"abc5cde"(lit"abc"^int^lit"cde")id-- Just 5-- The format specification is first-class. One can build format specification-- incrementally-- This is not the case with OCaml's printf/scanf (where the -- format specification has a weird typing and is not first class).-- Unit is to keep the type of fmt3 polymorphic and away from the-- monomorphism restrictionfmt50()=lit"abc"^int^lit"cde"fmt5()=fmt50()^fmt(undefined::Float)^chartp5=sprintf(fmt5())515'c'-- "abc5cde15.0c"ts5=sscanf"abc5cde15.0c"(fmt5())(\ifc->(i,f,c))-- Just (5,15.0,'c')-- Utility functions-- Primitive Printer/parsersshowread::(Showa,Reada)=>PrinterParserashowread=PrinterParsershowparsewhereparses=casereadssof[(v,s')]->Just(v,s')_->Nothing-- A better prefixOf function-- prefix patt str --> Just str'-- if the String patt is the prefix of String str. The result str'-- is str with patt removed-- Otherwise, the result is Nothingprefix::String->String->MaybeStringprefix""str=Juststrprefix(pc:pr)(sc:sr)|pc==sc=prefixprsrprefix__=Nothing