无法使用s表达式

4

我正在跟随Real World OCaml学习这门语言,其中有一点是在模块签名中使用s表达式。以下是我的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 已经安装,书籍和我在互联网上的搜索也没有提到使用这种语法需要任何“先决条件”。

我感觉好像漏掉了什么。有什么想法吗?:(


with sexp 语法依赖于 camlp4 扩展 -- 在使用之前应该在书中解释清楚。现代的做法是使用 ppx 扩展机制。我相信 Jane Street 已经发布了所有必要的 ppx 版本。由于这是一项新的语言特性,所以书中没有解释。我不知道去哪里查找如何实现它 -- 我猜 Google 是你的朋友。 - user3240588
展示你是如何编译它的,你使用了哪个命令? - user1971598
如果您想了解更多信息,这是第10章:一流模块。mli文件名为Query_Handler.mli,而ml文件名为Query_Handler.ml。我使用corebuild Query_Handler.native进行编译。此外,我使用的是ArchLinux,并安装了camlp4软件包。 - Richard-Degenne
2个回答

3
这是因为sexp库已被重写以使用扩展点,而不是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 中还是无法使用 :( 我需要加载特定的库吗? - Richard-Degenne
旁边的问题:我应该联系作者指出这一点吗? - Richard-Degenne
我认为这很有帮助。 - user962278

1
这是我的~/.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 已经不再使用了,是吗? - Richard-Degenne
当我深入挖掘时,正常的语法扩展应该使用“扩展点”。Camlp4现在已更名为Camlp5。它应该用于开发与OCaml不太相似的语言。http://pauillac.inria.fr/~ddr/camlp5/doc/html/ - user962278

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接