【问题标题】:Unable to use s-expressions无法使用 s 表达式
【发布时间】:2016-04-27 17:08:00
【问题描述】:

我正在关注 Real World OCaml 以开始使用该语言,并且在某一时刻,我将在模块签名中使用 s-expressions。这是我的mli 文件:

open Core.Std

(** Configuration type for query handlers *)
type config with sexp

(** Name of the query handler *)
val name : string

(** Query handler abstract type *)
type t

(** Create a query handler from an existing [config] *)
val create : config -> t

(** Evaluate a query, where both input and output an s-expressions. *)
val eval : t -> Sexp.t -> Sexp.t Or_error.t

但是,在编译该接口的实现时,我收到以下错误:

File "Query_Handler.mli", line 4, characters 12-16:
Error: Syntax error
Command exited with code 2.

所以我打开utop 来尝试with sexp 一个更简单的例子:

module type Test = sig
  type t with sexp
end;;

但我收到以下错误:

Error: Parse Error: "end" expected after [sig_items] (in [module type])

但是,sexplib 已安装,并且本书和我在 Internet 上的搜索均未提及使用此语法的任何“先决条件”。

我觉得我错过了什么。任何的想法? :(

【问题讨论】:

  • with sexp 语法依赖于camlp4 扩展——这应该在使用之前在书中的某个地方进行解释。执行此操作的现代方法是改用 ppx 扩展机制。我相信简街也有所有必要的东西作为 ppx 版本发布。这在书中没有解释,因为它是一个新的语言特性。不过我不知道在哪里查找如何操作——我猜 Google 是你的朋友。
  • 显示你是如何编译它的,你使用什么命令?
  • 如果您想了解更多信息,请参阅第 10 章:一流模块。 mli 称为 Query_Handler.mlimlQuery_Handler.ml。我使用corebuild Query_Handler.native 编译它。另外,我使用 ArchLinux 并且安装了包camlp4

标签: ocaml s-expression


【解决方案1】:

这是因为 sexp 库已被重写为使用 Extension Point, instead of Camlp4.

open Core.Std

module type Query_handler = sig
  (** Configuration for a query handler. Note that this can be
      Converted to and from an s-expression *)
  type config [@@deriving sexp]

  (** The name of the query-handling service *)
  val name : string

  (** The state of the query handler *)
  type t

  (** Create a new query handler from a config *)
  val create : config -> t

  (** Evaluate a given query, where both input and output are
      s-expressions *)
  val eval : t -> Sexp.t -> Sexp.t Or_error.t
end

module Unique = struct
  type config = int [@@deriving sexp]
  type t = { mutable next_id: int }

  let name = "unique"
  let create start_at = { next_id = start_at }

  let eval t sexp =
    match Or_error.try_with (fun () -> unit_of_sexp sexp) with
    | Error _ as err -> err
    | Ok () ->
       let response = Ok (Int.sexp_of_t t.next_id) in
       t.next_id <- t.next_id + 1;
       response
end

module List_dir = struct
  type config = string [@@deriving sexp]
  type t = { cwd: string }

  (** [is_abs p] Returns true if [p] is an absolute path *)
  let is_abs p =
    String.length p > 0 && p.[0] = '/'

  let name = "ls"
  let create cwd = { cwd }

  let eval t sexp =
    match Or_error.try_with (fun () -> string_of_sexp sexp) with
    | Error _ as err -> err
    | Ok dir ->
       let dir =
         if is_abs dir then dir
         else Filename.concat t.cwd dir
       in
       Ok (Array.sexp_of_t String.sexp_of_t (Sys.readdir dir))
end

module type Query_handler_instance = sig
  module Query_handler : Query_handler
  val this : Query_handler.t
end

let build_instance (type a)
                   (module Q : Query_handler with type config = a)
                   config =
  (module struct
     module Query_handler = Q
     let this = Q.create config
   end : Query_handler_instance)

let build_dispatch_table handlers =
  let table = String.Table.create () in
  List.iter handlers
            ~f:(fun ((module I : Query_handler_instance) as instance) ->
              Hashtbl.replace table ~key:I.Query_handler.name ~data:instance);
  table

let dispatch dispatch_table name_and_query =
  match name_and_query with
  | Sexp.List [Sexp.Atom name; query] ->
     begin match Hashtbl.find dispatch_table name with
     | None ->
        Or_error.error "Could not find matching handler"
                       name String.sexp_of_t
     | Some (module I : Query_handler_instance) ->
        I.Query_handler.eval I.this query
     end
  | _ ->
     Or_error.error_string "malformed query"

let rec cli dispatch_table =
  printf ">>> %!";
  let result =
    match In_channel.input_line stdin with
    | None      -> `Stop
    | Some line ->
       match Or_error.try_with (fun () -> Sexp.of_string line) with
       | Error e -> `Continue (Error.to_string_hum e)
       | Ok query ->
          begin match dispatch dispatch_table query with
          | Error e -> `Continue (Error.to_string_hum e)
          | Ok s    -> `Continue (Sexp.to_string_hum s)
          end;
  in
  match result with
  | `Stop         -> ()
  | `Continue msg ->
     printf "%s\n%!" msg;
     cli dispatch_table

let unique_instance = build_instance (module Unique) 0
let list_dir_instance = build_instance (module List_dir) "/var"                                    

module Loader = struct
  type config = (module Query_handler) list sexp_opaque [@@deriving sexp]

  type t = { known  : (module Query_handler)          String.Table.t
           ; active : (module Query_handler_instance) String.Table.t
           }

  let name ="loader"

  let create known_list =
    let active = String.Table.create () in
    let known  = String.Table.create () in
    List.iter known_list
              ~f:(fun ((module Q : Query_handler) as q) ->
                Hashtbl.replace known ~key:Q.name ~data:q);
    { known; active }

  let load t handler_name config =
    if Hashtbl.mem t.active handler_name then
      Or_error.error "Can't re-register an active handler"
                     handler_name String.sexp_of_t
    else
      match Hashtbl.find t.known handler_name with
      | None ->
         Or_error.error "Unknown handler" handler_name String.sexp_of_t
      | Some (module Q : Query_handler) ->
         let instance =
           (module struct
              module Query_handler = Q
              let this = Q.create (Q.config_of_sexp config)
            end : Query_handler_instance)
         in
         Hashtbl.replace t.active ~key:handler_name ~data:instance;
         Ok Sexp.unit

  let unload t handler_name =
    if not (Hashtbl.mem t.active handler_name) then
      Or_error.error "Handler not active" handler_name String.sexp_of_t
    else if handler_name = name then
      Or_error.error_string "It's unwise to unload yourself"
    else (
      Hashtbl.remove t.active handler_name;
      Ok Sexp.unit
    )

  type request =
    | Load of string * Sexp.t
    | Unload of string
    | Known_services
    | Active_services [@@deriving sexp]

  let eval t sexp =
    match Or_error.try_with (fun () -> request_of_sexp sexp) with
    | Error _ as err -> err
    | Ok resp ->
       match resp with
       | Load (name,config) -> load t name config
       | Unload name        -> unload t name
       | Known_services     ->
          Ok [%sexp ((Hashtbl.keys t.known ) : string list)]
       | Active_services ->
          Ok [%sexp ((Hashtbl.keys t.active) : string list)]
end

【讨论】:

  • 是的,成功了,非常感谢!不过,它在utop 中仍然不起作用。 :( 我需要为此加载特定的库吗?
  • 附带问题:我应该联系作者指出这一点吗?
  • 我觉得很有帮助。
【解决方案2】:

这是我的 ~/.ocamlinit;只需注释掉camlp4。 utop 应该工作愉快。

#use "topfind";;
#warnings "+9"
#thread;;
(*camlp4;;*)
#require "core.top";;
#require "core_extended";;
#require "core_bench";;
#require "ppx_jane";;
#require "ctypes";;
#require "ctypes.foreign";;

【讨论】:

  • 是的,也成功了,再次感谢。所以我认为camlp4根本不再使用了?
  • 我在挖掘。正常的语法扩展应该使用“扩展点”。 Camlp4 现在已重命名为 Camlp5。它应该用于开发不太像 OCaml 的语言。 pauillac.inria.fr/~ddr/camlp5/doc/html
猜你喜欢
  • 1970-01-01
  • 2016-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-26
  • 1970-01-01
  • 2018-03-02
  • 1970-01-01
相关资源
最近更新 更多