Merge ppx_let
This commit is contained in:
commit
db2cc00626
5
src/ppx_let/.gitignore
vendored
Normal file
5
src/ppx_let/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
_build
|
||||
*.install
|
||||
*.merlin
|
||||
_opam
|
||||
|
17
src/ppx_let/CHANGES.md
Normal file
17
src/ppx_let/CHANGES.md
Normal file
@ -0,0 +1,17 @@
|
||||
## git version
|
||||
|
||||
- Support for `%map.A.B.C` syntax to use values from a specific module, rather
|
||||
than the one in scope.
|
||||
|
||||
## v0.11
|
||||
|
||||
- Depend on ppxlib instead of (now deprecated) ppx\_core and ppx\_driver.
|
||||
|
||||
## 113.43.00
|
||||
|
||||
- Dropped `Open_in_body` support from ppx\_let, since it was only ever used
|
||||
in confusing chains of `Let_syntax` modules that introduced other
|
||||
`Let_syntax` modules in the "body" (e.g. for defining Commands whose
|
||||
bodies use Async). In this case it was decided that the better
|
||||
practice is to be explicit with `open ___.Let_syntax` at the different
|
||||
transition points, even though this is more verbose.
|
67
src/ppx_let/CONTRIBUTING.md
Normal file
67
src/ppx_let/CONTRIBUTING.md
Normal file
@ -0,0 +1,67 @@
|
||||
This repository contains open source software that is developed and
|
||||
maintained by [Jane Street][js].
|
||||
|
||||
Contributions to this project are welcome and should be submitted via
|
||||
GitHub pull requests.
|
||||
|
||||
Signing contributions
|
||||
---------------------
|
||||
|
||||
We require that you sign your contributions. Your signature certifies
|
||||
that you wrote the patch or otherwise have the right to pass it on as
|
||||
an open-source patch. The rules are pretty simple: if you can certify
|
||||
the below (from [developercertificate.org][dco]):
|
||||
|
||||
```
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
1 Letterman Drive
|
||||
Suite D4700
|
||||
San Francisco, CA, 94129
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
Then you just add a line to every git commit message:
|
||||
|
||||
```
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
```
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can sign
|
||||
your commit automatically with git commit -s.
|
||||
|
||||
[dco]: http://developercertificate.org/
|
||||
[js]: https://opensource.janestreet.com/
|
4
src/ppx_let/CREDITS
Normal file
4
src/ppx_let/CREDITS
Normal file
@ -0,0 +1,4 @@
|
||||
This folder contains a generalization of ppx_let from Jane Street.
|
||||
See git log this_folder for the development history.
|
||||
|
||||
https://github.com/janestreet/ppx_let.git
|
21
src/ppx_let/LICENSE.md
Normal file
21
src/ppx_let/LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2015--2019 Jane Street Group, LLC <opensource@janestreet.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
17
src/ppx_let/Makefile
Normal file
17
src/ppx_let/Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
INSTALL_ARGS := $(if $(PREFIX),--prefix $(PREFIX),)
|
||||
|
||||
default:
|
||||
dune build
|
||||
|
||||
install:
|
||||
dune install $(INSTALL_ARGS)
|
||||
|
||||
uninstall:
|
||||
dune uninstall $(INSTALL_ARGS)
|
||||
|
||||
reinstall: uninstall install
|
||||
|
||||
clean:
|
||||
dune clean
|
||||
|
||||
.PHONY: default install uninstall reinstall clean
|
169
src/ppx_let/README.md
Normal file
169
src/ppx_let/README.md
Normal file
@ -0,0 +1,169 @@
|
||||
ppx_let
|
||||
=======
|
||||
|
||||
A ppx rewriter for monadic and applicative let bindings, match expressions, and
|
||||
if expressions.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The aim of this rewriter is to make monadic and applicative code look nicer by
|
||||
writing custom binders the same way that we normally bind variables. In OCaml,
|
||||
the common way to bind the result of a computation to a variable is:
|
||||
|
||||
```ocaml
|
||||
let VAR = EXPR in BODY
|
||||
```
|
||||
|
||||
ppx\_let simply adds two new binders: `let%bind` and `let%map`. These are
|
||||
rewritten into calls to the `bind` and `map` functions respectively. These
|
||||
functions are expected to have
|
||||
|
||||
```ocaml
|
||||
val map : 'a t -> f:('a -> 'b) -> 'b t
|
||||
val bind : 'a t -> f:('a -> 'b t) -> 'b t
|
||||
```
|
||||
|
||||
for some type `t`, as one might expect.
|
||||
|
||||
These functions are to be provided by the user, and are generally expected to be
|
||||
part of the signatures of monads and applicatives modules. This is the case for
|
||||
all monads and applicatives defined by the Jane Street's Core suite of
|
||||
libraries. (see the section below on getting the right names into scope).
|
||||
|
||||
### Parallel bindings
|
||||
|
||||
ppx\_let understands parallel bindings as well. i.e.:
|
||||
|
||||
```ocaml
|
||||
let%bind VAR1 = EXPR1 and VAR2 = EXPR2 and VAR3 = EXPR3 in BODY
|
||||
```
|
||||
|
||||
The `and` keyword is seen as a binding combination operator. To do so it expects
|
||||
the presence of a `both` function, that lifts the OCaml pair operation to the
|
||||
type `t` in question:
|
||||
|
||||
```ocaml
|
||||
val both : 'a t -> 'b t -> ('a * 'b) t
|
||||
```
|
||||
|
||||
### Match statements
|
||||
|
||||
We found that this form was quite useful for match statements as well. So for
|
||||
convenience ppx\_let also accepts `%bind` and `%map` on the `match` keyword.
|
||||
Morally `match%bind expr with cases` is seen as `let%bind x = expr in match x
|
||||
with cases`.
|
||||
|
||||
### If statements
|
||||
|
||||
As a further convenience, ppx\_let accepts `%bind` and `%map` on the `if`
|
||||
keyword. The expression `if%bind expr1 then expr2 else expr3` is morally
|
||||
equivalent to `let%bind p = expr1 in if p then expr2 else expr3`.
|
||||
|
||||
Syntactic forms and actual rewriting
|
||||
------------------------------------
|
||||
|
||||
`ppx_let` adds six syntactic forms
|
||||
|
||||
```ocaml
|
||||
let%bind P = M in E
|
||||
|
||||
let%map P = M in E
|
||||
|
||||
match%bind M with P1 -> E1 | P2 -> E2 | ...
|
||||
|
||||
match%map M with P1 -> E1 | P2 -> E2 | ...
|
||||
|
||||
if%bind M then E1 else E2
|
||||
|
||||
if%map M then E1 else E2
|
||||
```
|
||||
|
||||
that expand into
|
||||
|
||||
```ocaml
|
||||
bind M ~f:(fun P -> E)
|
||||
|
||||
map M ~f:(fun P -> E)
|
||||
|
||||
bind M ~f:(function P1 -> E1 | P2 -> E2 | ...)
|
||||
|
||||
map M ~f:(function P1 -> E1 | P2 -> E2 | ...)
|
||||
|
||||
bind M ~f:(function true -> E1 | false -> E2)
|
||||
|
||||
map M ~f:(function true -> E1 | false -> E2)
|
||||
```
|
||||
|
||||
respectively.
|
||||
|
||||
As with `let`, `let%bind` and `let%map` also support multiple *parallel*
|
||||
bindings via the `and` keyword:
|
||||
|
||||
```ocaml
|
||||
let%bind P1 = M1 and P2 = M2 and P3 = M3 and P4 = M4 in E
|
||||
|
||||
let%map P1 = M1 and P2 = M2 and P3 = M3 and P4 = M4 in E
|
||||
```
|
||||
|
||||
that expand into
|
||||
|
||||
```ocaml
|
||||
let x1 = M1 and x2 = M2 and x3 = M3 and x4 = M4 in
|
||||
bind
|
||||
(both x1 (both x2 (both x3 x4)))
|
||||
~f:(fun (P1, (P2, (P3, P4))) -> E)
|
||||
|
||||
let x1 = M1 and x2 = M2 and x3 = M3 and x4 = M4 in
|
||||
map
|
||||
(both x1 (both x2 (both x3 x4)))
|
||||
~f:(fun (P1, (P2, (P3, P4))) -> E)
|
||||
```
|
||||
|
||||
respectively. (Instead of `x1`, `x2`, ... ppx\_let uses variable names that are
|
||||
unlikely to clash with other names)
|
||||
|
||||
As with `let`, names introduced by left-hand sides of the let bindings are not
|
||||
available in subsequent right-hand sides of the same sequence.
|
||||
|
||||
Getting the right names in scope
|
||||
--------------------------------
|
||||
|
||||
The description of how the `%bind` and `%map` syntax extensions expand left out
|
||||
the fact that the names `bind`, `map`, `both`, and `return` are not used
|
||||
directly., but rather qualified by `Let_syntax`. For example, we use
|
||||
`Let_syntax.bind` rather than merely `bind`.
|
||||
|
||||
This means one just needs to get a properly loaded `Let_syntax` module
|
||||
in scope to use `%bind` and `%map`.
|
||||
|
||||
Alternatively, the extension can use values from a `Let_syntax` module
|
||||
other than the one in scope. If you write `%map.A.B.C` instead of
|
||||
`%map`, the expansion will use `A.B.C.Let_syntax.map` instead of
|
||||
`Let_syntax.map` (and similarly for all extension points).
|
||||
|
||||
For monads, `Core.Monad.Make` produces a submodule `Let_syntax` of the
|
||||
appropriate form.
|
||||
|
||||
For applicatives, the convention for these modules is to have a submodule
|
||||
`Let_syntax` of the form:
|
||||
|
||||
```ocaml
|
||||
module Let_syntax : sig
|
||||
val return : 'a -> 'a t
|
||||
val map : 'a t -> f:('a -> 'b) -> 'b t
|
||||
val both : 'a t -> 'b t -> ('a * 'b) t
|
||||
module Open_on_rhs : << some signature >>
|
||||
end
|
||||
```
|
||||
|
||||
The `Open_on_rhs` submodule is used by variants of `%map` and `%bind` called
|
||||
`%map_open` and `%bind_open`. It is locally opened on the right hand sides of
|
||||
the rewritten let bindings in `%map_open` and `%bind_open` expressions. For
|
||||
`match%map_open` and `match%bind_open` expressions, `Open_on_rhs` is opened for
|
||||
the expression being matched on.
|
||||
|
||||
`Open_on_rhs` is useful when programming with applicatives, which operate in a
|
||||
staged manner where the operators used to construct the applicatives are
|
||||
distinct from the operators used to manipulate the values those applicatives
|
||||
produce. For monads, `Open_on_rhs` contains `return`.
|
0
src/ppx_let/dune
Normal file
0
src/ppx_let/dune
Normal file
1
src/ppx_let/dune-project
Normal file
1
src/ppx_let/dune-project
Normal file
@ -0,0 +1 @@
|
||||
(lang dune 1.5)
|
2
src/ppx_let/expander/dune
Normal file
2
src/ppx_let/expander/dune
Normal file
@ -0,0 +1,2 @@
|
||||
(library (name ppx_let_expander) (public_name ppx_let.expander)
|
||||
(libraries base ppxlib) (preprocess no_preprocessing))
|
155
src/ppx_let/expander/ppx_let_expander.ml
Normal file
155
src/ppx_let/expander/ppx_let_expander.ml
Normal file
@ -0,0 +1,155 @@
|
||||
open Base
|
||||
open Ppxlib
|
||||
open Ast_builder.Default
|
||||
|
||||
module List = struct
|
||||
include List
|
||||
|
||||
let reduce_exn l ~f =
|
||||
match l with
|
||||
| [] -> invalid_arg "List.reduce_exn"
|
||||
| hd :: tl -> fold_left tl ~init:hd ~f
|
||||
;;
|
||||
end
|
||||
|
||||
let let_syntax ~modul : Longident.t =
|
||||
match modul with
|
||||
| None -> Lident "Let_syntax"
|
||||
| Some id -> Ldot (id.txt, "Let_syntax")
|
||||
;;
|
||||
|
||||
let open_on_rhs ~loc ~modul ~extension_name_s =
|
||||
Located.mk ~loc (Longident.Ldot (let_syntax ~modul, "Open_on_rhs_" ^ extension_name_s))
|
||||
;;
|
||||
|
||||
let eoperator ~loc ~modul func =
|
||||
let lid : Longident.t = Ldot (let_syntax ~modul, func) in
|
||||
pexp_ident ~loc (Located.mk ~loc lid)
|
||||
;;
|
||||
|
||||
let expand_with_tmp_vars ~loc bindings expr ~f =
|
||||
match bindings with
|
||||
| [ _ ] -> f ~loc bindings expr
|
||||
| _ ->
|
||||
let tmp_vars =
|
||||
List.map bindings ~f:(fun _ -> gen_symbol ~prefix:"__let_syntax" ())
|
||||
in
|
||||
let s_rhs_tmp_var (* s/rhs/tmp_var *) =
|
||||
List.map2_exn bindings tmp_vars ~f:(fun vb var ->
|
||||
{ vb with pvb_expr = evar ~loc:vb.pvb_expr.pexp_loc var })
|
||||
in
|
||||
let s_lhs_tmp_var (* s/lhs/tmp_var *) =
|
||||
List.map2_exn bindings tmp_vars ~f:(fun vb var ->
|
||||
{ vb with pvb_pat = pvar ~loc:vb.pvb_pat.ppat_loc var })
|
||||
in
|
||||
pexp_let ~loc Nonrecursive s_lhs_tmp_var (f ~loc s_rhs_tmp_var expr)
|
||||
;;
|
||||
|
||||
let bind_apply ~loc ~modul extension_name_s ~arg ~fn =
|
||||
pexp_apply
|
||||
~loc
|
||||
(eoperator ~loc ~modul extension_name_s)
|
||||
[ Nolabel, arg; Labelled "f", fn ]
|
||||
;;
|
||||
|
||||
(* Change by Georges: Always open for all extension names. *)
|
||||
let maybe_open ~to_open:module_to_open expr =
|
||||
let loc = expr.pexp_loc in
|
||||
pexp_open ~loc Override (module_to_open ~loc) expr
|
||||
;;
|
||||
|
||||
let expand_let extension_name_s ~loc ~modul bindings body =
|
||||
if List.is_empty bindings
|
||||
then invalid_arg "expand_let: list of bindings must be non-empty";
|
||||
(* Build expression [both E1 (both E2 (both ...))] *)
|
||||
let nested_boths =
|
||||
let rev_boths = List.rev_map bindings ~f:(fun vb -> vb.pvb_expr) in
|
||||
List.reduce_exn rev_boths ~f:(fun acc e ->
|
||||
let loc = e.pexp_loc in
|
||||
eapply ~loc (eoperator ~loc ~modul "both") [ e; acc ])
|
||||
in
|
||||
(* Build pattern [(P1, (P2, ...))] *)
|
||||
let nested_patterns =
|
||||
let rev_patts = List.rev_map bindings ~f:(fun vb -> vb.pvb_pat) in
|
||||
List.reduce_exn rev_patts ~f:(fun acc p ->
|
||||
let loc = p.ppat_loc in
|
||||
ppat_tuple ~loc [ p; acc ])
|
||||
in
|
||||
bind_apply
|
||||
~loc
|
||||
~modul
|
||||
extension_name_s
|
||||
~arg:nested_boths
|
||||
~fn:(pexp_fun ~loc Nolabel None nested_patterns body)
|
||||
;;
|
||||
|
||||
let expand_match extension_name_s ~loc ~modul expr cases =
|
||||
bind_apply
|
||||
~loc
|
||||
~modul
|
||||
extension_name_s
|
||||
~arg:(maybe_open ~to_open:(open_on_rhs ~modul ~extension_name_s) expr)
|
||||
~fn:(pexp_function ~loc cases)
|
||||
;;
|
||||
|
||||
let expand_if extension_name ~loc expr then_ else_ =
|
||||
expand_match
|
||||
extension_name
|
||||
~loc
|
||||
expr
|
||||
[ case ~lhs:(pbool ~loc true) ~guard:None ~rhs:then_
|
||||
; case ~lhs:(pbool ~loc false) ~guard:None ~rhs:else_
|
||||
]
|
||||
;;
|
||||
|
||||
let expand ~modul extension_name_s expr =
|
||||
let loc = expr.pexp_loc in
|
||||
let expansion =
|
||||
match expr.pexp_desc with
|
||||
| Pexp_let (Nonrecursive, bindings, expr) ->
|
||||
let bindings =
|
||||
List.map bindings ~f:(fun vb ->
|
||||
let pvb_pat =
|
||||
(* Temporary hack tentatively detecting that the parser
|
||||
has expanded `let x : t = e` into `let x : t = (e : t)`.
|
||||
|
||||
For reference, here is the relevant part of the parser:
|
||||
https://github.com/ocaml/ocaml/blob/4.07/parsing/parser.mly#L1628 *)
|
||||
match vb.pvb_pat.ppat_desc, vb.pvb_expr.pexp_desc with
|
||||
| ( Ppat_constraint (p, { ptyp_desc = Ptyp_poly ([], t1); _ })
|
||||
, Pexp_constraint (_, t2) )
|
||||
when phys_equal t1 t2 -> p
|
||||
| _ -> vb.pvb_pat
|
||||
in
|
||||
{ vb with
|
||||
pvb_pat
|
||||
; pvb_expr =
|
||||
maybe_open ~to_open:(open_on_rhs ~modul ~extension_name_s) vb.pvb_expr
|
||||
})
|
||||
in
|
||||
expand_with_tmp_vars ~loc bindings expr ~f:(expand_let extension_name_s ~modul)
|
||||
| Pexp_let (Recursive, _, _) ->
|
||||
Location.raise_errorf
|
||||
~loc
|
||||
"'let%%%s' may not be recursive"
|
||||
extension_name_s
|
||||
| Pexp_match (expr, cases) -> expand_match extension_name_s ~loc ~modul expr cases
|
||||
| Pexp_ifthenelse (expr, then_, else_) ->
|
||||
let else_ =
|
||||
match else_ with
|
||||
| Some else_ -> else_
|
||||
| None ->
|
||||
Location.raise_errorf
|
||||
~loc
|
||||
"'if%%%s' must include an else branch"
|
||||
extension_name_s
|
||||
in
|
||||
expand_if extension_name_s ~loc ~modul expr then_ else_
|
||||
| _ ->
|
||||
Location.raise_errorf
|
||||
~loc
|
||||
"'%%%s' can only be used with 'let', 'match', and 'if'"
|
||||
extension_name_s
|
||||
in
|
||||
{ expansion with pexp_attributes = expr.pexp_attributes @ expansion.pexp_attributes }
|
||||
;;
|
3
src/ppx_let/expander/ppx_let_expander.mli
Normal file
3
src/ppx_let/expander/ppx_let_expander.mli
Normal file
@ -0,0 +1,3 @@
|
||||
open Ppxlib
|
||||
|
||||
val expand : modul:longident loc option -> string -> expression -> expression
|
21
src/ppx_let/ppx_let.opam
Normal file
21
src/ppx_let/ppx_let.opam
Normal file
@ -0,0 +1,21 @@
|
||||
opam-version: "2.0"
|
||||
maintainer: "opensource@janestreet.com"
|
||||
authors: ["Jane Street Group, LLC <opensource@janestreet.com>"]
|
||||
homepage: "https://github.com/janestreet/ppx_let"
|
||||
bug-reports: "https://github.com/janestreet/ppx_let/issues"
|
||||
dev-repo: "git+https://github.com/janestreet/ppx_let.git"
|
||||
doc: "https://ocaml.janestreet.com/ocaml-core/latest/doc/ppx_let/index.html"
|
||||
license: "MIT"
|
||||
build: [
|
||||
["dune" "build" "-p" name "-j" jobs]
|
||||
]
|
||||
depends: [
|
||||
"ocaml" {>= "4.04.2" & < "4.08.0"}
|
||||
"base"
|
||||
"dune" {build & >= "1.5.1"}
|
||||
"ppxlib" {>= "0.5.0"}
|
||||
]
|
||||
synopsis: "Monadic let-bindings"
|
||||
description: "
|
||||
Part of the Jane Street's PPX rewriters collection.
|
||||
"
|
2
src/ppx_let/src/dune
Normal file
2
src/ppx_let/src/dune
Normal file
@ -0,0 +1,2 @@
|
||||
(library (name ppx_let) (public_name ppx_let) (kind ppx_rewriter)
|
||||
(libraries ppxlib ppx_let_expander) (preprocess no_preprocessing))
|
19
src/ppx_let/src/ppx_let.ml
Normal file
19
src/ppx_let/src/ppx_let.ml
Normal file
@ -0,0 +1,19 @@
|
||||
open Ppxlib
|
||||
|
||||
let ext extension_name_s =
|
||||
Extension.declare_with_path_arg
|
||||
extension_name_s
|
||||
Extension.Context.expression
|
||||
Ast_pattern.(single_expr_payload __)
|
||||
(fun ~loc:_ ~path:_ ~arg expr ->
|
||||
Ppx_let_expander.expand extension_name_s ~modul:arg expr)
|
||||
;;
|
||||
|
||||
let () =
|
||||
Driver.register_transformation
|
||||
"let"
|
||||
~extensions:(List.map ext [
|
||||
"bind";
|
||||
"xxx";
|
||||
])
|
||||
;;
|
1
src/ppx_let/src/ppx_let.mli
Normal file
1
src/ppx_let/src/ppx_let.mli
Normal file
@ -0,0 +1 @@
|
||||
|
1
src/ppx_let/test/dune
Normal file
1
src/ppx_let/test/dune
Normal file
@ -0,0 +1 @@
|
||||
(executables (names test) (preprocess (pps ppx_let)))
|
27
src/ppx_let/test/test-locations.mlt
Normal file
27
src/ppx_let/test/test-locations.mlt
Normal file
@ -0,0 +1,27 @@
|
||||
(* -*- tuareg -*- *)
|
||||
|
||||
module Let_syntax = struct
|
||||
type 'a t = T of 'a
|
||||
|
||||
let map (T x) ~f = T (f x)
|
||||
let both (T x) (T y) = T (x, y)
|
||||
|
||||
module Open_on_rhs = struct
|
||||
let return x = T x
|
||||
let f x ~(doc : string) = T (x, doc)
|
||||
end
|
||||
end
|
||||
|
||||
let _ =
|
||||
[%map_open
|
||||
let x = return 42
|
||||
and y = f 42 in
|
||||
()]
|
||||
;;
|
||||
|
||||
[%%expect
|
||||
{|
|
||||
Line _, characters 12-16:
|
||||
Error: This expression has type doc:string -> (int * string) Let_syntax.t
|
||||
but an expression was expected of type 'a Let_syntax.t
|
||||
|}]
|
189
src/ppx_let/test/test.ml
Normal file
189
src/ppx_let/test/test.ml
Normal file
@ -0,0 +1,189 @@
|
||||
module Monad_example = struct
|
||||
module Let_syntax = struct
|
||||
let bind x ~f = f x
|
||||
module Open_on_rhs_bind = struct
|
||||
let return _ = "foo"
|
||||
end
|
||||
end
|
||||
|
||||
let _mf a =
|
||||
let%bind xyz = return a in
|
||||
(int_of_string xyz + 1)
|
||||
;;
|
||||
end
|
||||
|
||||
(* TODO: re-enable some tests *)
|
||||
|
||||
(*
|
||||
module Monad_example = struct
|
||||
module X : sig
|
||||
type 'a t
|
||||
|
||||
module Let_syntax : sig
|
||||
val return : 'a -> 'a t
|
||||
|
||||
module Let_syntax : sig
|
||||
val return : 'a -> 'a t
|
||||
val bind : 'a t -> f:('a -> 'b t) -> 'b t
|
||||
val map : 'a t -> f:('a -> 'b) -> 'b t
|
||||
val both : 'a t -> 'b t -> ('a * 'b) t
|
||||
|
||||
module Open_on_rhs : sig
|
||||
val return : 'a -> 'a t
|
||||
end
|
||||
end
|
||||
end
|
||||
end = struct
|
||||
type 'a t = 'a
|
||||
|
||||
let return x = x
|
||||
let bind x ~f = f x
|
||||
let map x ~f = f x
|
||||
let both x y = x, y
|
||||
|
||||
module Let_syntax = struct
|
||||
let return = return
|
||||
|
||||
module Let_syntax = struct
|
||||
let return = return
|
||||
let bind = bind
|
||||
let map = map
|
||||
let both = both
|
||||
|
||||
module Open_on_rhs = struct
|
||||
let return = return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
open X.Let_syntax
|
||||
|
||||
let _mf a : _ X.t =
|
||||
let%bind_open x = a in
|
||||
return (x + 1)
|
||||
;;
|
||||
|
||||
let _mf' a b c : _ X.t =
|
||||
let%bind_open x = a
|
||||
and y = b
|
||||
and u, v = c in
|
||||
return (x + y + (u * v))
|
||||
;;
|
||||
|
||||
let _mg a : _ X.t =
|
||||
let%map x : int X.t = a in
|
||||
x + 1
|
||||
;;
|
||||
|
||||
let _mg' a b c : _ X.t =
|
||||
let%map x = a
|
||||
and y = b
|
||||
and u, v = c in
|
||||
x + y + (u * v)
|
||||
;;
|
||||
|
||||
let _mh a : _ X.t =
|
||||
match%bind_open a with
|
||||
| 0 -> return true
|
||||
| _ -> return false
|
||||
;;
|
||||
|
||||
let _mi a : _ X.t =
|
||||
match%map a with
|
||||
| 0 -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let _mif a : _ X.t = if%bind_open a then return true else return false
|
||||
let _mif' a : _ X.t = if%map a then true else false
|
||||
end
|
||||
|
||||
module Applicative_example = struct
|
||||
module X : sig
|
||||
type 'a t
|
||||
|
||||
module Let_syntax : sig
|
||||
val return : 'a -> 'a t
|
||||
|
||||
module Let_syntax : sig
|
||||
val return : 'a -> 'a t
|
||||
val map : 'a t -> f:('a -> 'b) -> 'b t
|
||||
val both : 'a t -> 'b t -> ('a * 'b) t
|
||||
|
||||
module Open_on_rhs : sig
|
||||
val flag : int t
|
||||
val anon : int t
|
||||
end
|
||||
end
|
||||
end
|
||||
end = struct
|
||||
type 'a t = 'a
|
||||
|
||||
let return x = x
|
||||
let map x ~f = f x
|
||||
let both x y = x, y
|
||||
|
||||
module Let_syntax = struct
|
||||
let return = return
|
||||
|
||||
module Let_syntax = struct
|
||||
let return = return
|
||||
let map = map
|
||||
let both = both
|
||||
|
||||
module Open_on_rhs = struct
|
||||
let flag = 66
|
||||
let anon = 77
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
open X.Let_syntax
|
||||
|
||||
(* {[
|
||||
let _af a : _ X.t =
|
||||
let%bind x = a in (* "Error: Unbound value Let_syntax.bind" *)
|
||||
return (x + 1)
|
||||
]} *)
|
||||
|
||||
(* {[
|
||||
let _af' a b c : _ X.t =
|
||||
let%bind x = a and y = b and (u, v) = c in (* "Error: Unbound value Let_syntax.bind" *)
|
||||
return (x + y + (u * v))
|
||||
]} *)
|
||||
|
||||
let _ag a : _ X.t =
|
||||
let%map x = a in
|
||||
x + 1
|
||||
;;
|
||||
|
||||
let _ag' a b c : _ X.t =
|
||||
let%map x = a
|
||||
and y = b
|
||||
and u, v = c in
|
||||
x + y + (u * v)
|
||||
;;
|
||||
|
||||
(* {[
|
||||
let _ah a : _ X.t =
|
||||
match%bind a with (* "Error: Unbound value Let_syntax.bind" *)
|
||||
| 0 -> return true
|
||||
| _ -> return false
|
||||
]} *)
|
||||
|
||||
let _ai a : _ X.t =
|
||||
match%map a with
|
||||
| 0 -> true
|
||||
| _ -> false
|
||||
;;
|
||||
end
|
||||
|
||||
module Example_without_open = struct
|
||||
let _ag a : _ Applicative_example.X.t =
|
||||
let%map.Applicative_example.X.Let_syntax x = a in
|
||||
x + 1
|
||||
;;
|
||||
end
|
||||
*)
|
Loading…
Reference in New Issue
Block a user