Type extension in plugin architectures

Now I have a working HTML template system written in OCaml. The overall design is that a single template is a module returned by a functor applied to the following type of module:

module type TEMPLATE_DEF = sig
  type t (* The type of the data rendered by the template. *)
  val source : string (* Where the HTML template itself resides. *)
  val mapping : (string * (t -> string)) list 
end

For example, creating a blog post is based on this:

module Post = Loader.Html(struct
  type t = < body : string ; title : string >
  let source  = ...
  let mapping = [ "body", (fun x -> x#body) ; "title", (fun x -> x#title) ]
end)

More complex is simply the presence of a function t -> (string * string) listthat extracts all possible values, but during initialization, it ensures that all the necessary template variables are provided.

Adding a new field, for example permalink, is trivial, but requires manual code editing. I try to move away from this process in a situation where something permalinkrelated throughout the application is purely transferred in the module and simply applied wherever it should be used.

:

module WithPermalink = functor(Def:TEMPLATE_DEF) -> struct
  type t = < permalink : string ; inner : Def.t >
  let source = Def.source 
  let mapping =
    ( "permalink", (fun x -> x # permalink) ) 
    :: List.map (fun (k,f) -> (k, (fun x -> f (x#inner)))) Def.mapping 
end

- , .

, - ( WithPermalink). , , Post permalink (, , ).

, ( , , ...), , , , . , , .

?

, , . , :

(* Module Post *)
type post = {%
  title : string = "" ;
  body  : string = "" 
%}   

let mapping : (string * (post -> string)) list ref = 
  [ "title", (%title) ;
    "body",  (%body) ]

(* Module Permalink *)
type %extend Post.post = {% 
  link : string = "" 
%}

Post.mapping := ("permalink", (%link)) :: !Post.mapping

(* Defining the template *)
module BlogPost = Loader.Html(struct
  type t = Post.post
  let source = ...
  let mapping _ = !Post.mapping
end)

(* Creating and editing a post *)
let post = {% new Post.post with 
  Post.title     = get_title () ;
  Post.body      = get_body () ;
  Permalink.link = get_permalink () ; 
%}

let post' = {% post with title = BatString.strip (post % Post.title) %}

: Post, ExtenderImplementation_post :

module ExtenderImplementation_post : sig
  type t 
  val field : 'a -> (t,'a) lens
  val create : unit -> t
end = struct
  type t = (unit -> unit) array
  let fields : t ref = ref [| |]
  let field default =
    let store = ref None in
    let ctor () = store := Some default in
    let n = Array.length !fields in
    fields := Array.init (n+1) (fun i -> if i = n then ctor else (!fields).(i)) ;
    { lens_get = (fun (t:t) -> t.(n) () ; match !store with
      | None   -> assert false
      | Some s -> store := None ; s) ;
      lens_set = (fun x (t:t) -> let t' = Array.copy t in
                            t'.(n) <- (fun () -> store := Some x) ; t') }
  let create () = !fields
end
type post = ExtenderImplementation_post.t

link : string = "" :

let link : (Post.post,string) lens = Post.ExtenderImplementation_post.extend "" 

, , .

- ?

+5
1

, , , . , camlp4 ?

. , .

, , - , . Haskell HaskellTemplate, , , .

OCaml , , ocaml-3.12.0, . , , , OCaml AST AST ( , AST).

+3

All Articles