Heterogeneous key/value collection in Idris - idris

I have just started programming a heterogeneous key/value collection to better understand proofs. Here is my code:
data Collection : Type where
None : Collection
Cons : {a : Type} ->
(name : String) ->
(value : a) ->
Collection ->
Collection
example : Collection
example = Cons "str" "tralala" (Cons {a = Int} "num" 1 None)
data AllValues : (p : a -> Type) -> Collection -> Type where
First : AllValues p None
Next : p a => AllValues p col -> AllValues p (Cons {a} k v col)
printCollection : (col : Collection) -> {auto prf : AllValues Show col} -> String
printCollection None {prf = First} = "."
printCollection (Cons key value rest) {prf = (Next later)} =
"(" ++ key ++ ": " ++ show value ++ "), " ++ printCollection rest
--------------------------------------------------------------------------------
data IsNewKey : (key : String) -> Collection -> Type where
U1 : IsNewKey key None
U2 : Not (key = k) => IsNewKey key col -> IsNewKey key (Cons k value col)
insert : {a : Type} ->
(name : String) ->
(value : a) ->
{auto notIn : IsNewKey name col} ->
(col : Collection) ->
Collection
insert {a} name value col = Cons {a} name value col
data IsElement : String -> Collection -> Type where
Here : IsElement key (Cons key value rest)
There : (later : IsElement key rest) -> IsElement key (Cons name value rest)
update : (key : String) ->
(newVal : ty) ->
(col : Collection) ->
{auto prf : IsElement key col} ->
Collection
update key newVal (Cons key _ rest) {prf = Here} = Cons key newVal rest
update key newVal (Cons name value rest) {prf = (There later)} =
Cons name value (update key newVal rest)
GetType : (key : String) -> (col : Collection) -> {auto prf : IsElement key col} -> Type
GetType key (Cons {a} key _ _) {prf = Here} = a
GetType key (Cons _ _ rest) {prf = There later} = GetType key rest
get : (key : String) -> (col : Collection) -> {auto prf : IsElement key col} -> GetType key col
get key (Cons key value _) {prf = Here} = value
get key (Cons _ _ rest) {prf = There later} = get key rest
I have two problems here:
Somehow Not (key = k) does not work, I am able to insert duplicated
keys.
How to derive an instance for the Show? Where to put All
Show...? (Probably it is not possible)
Any ideas?

OK, finally I was able to fix insert function with an advice from michaelmesser.
getKeys : Collection -> List String
getKeys None = []
getKeys (Cons name _ rest) = name :: getKeys rest
fromFalse : (d : Dec p) -> {auto isFalse : decAsBool d = False} -> Not p
fromFalse (Yes _) {isFalse = Refl} impossible
fromFalse (No contra) = contra
data NotEq : a -> a -> Type where
MkNotEq : {a : t} -> {b : t} -> Not (a = b) -> NotEq a b
%hint
notEq : DecEq t => {a : t} -> {b : t} -> {auto isFalse : decAsBool (decEq a b) = False} -> NotEq a b
notEq = MkNotEq (fromFalse (decEq _ _))
insert : {a : Type} ->
(name : String) ->
(value : a) ->
(col : Collection) ->
{auto prf : All (NotEq name) (getKeys col)} ->
Collection
insert {a} name value col = Cons {a} name value col
I also added another useful functions: delete, decompose, hasKey, upsert.
infixr 7 .::
(.::) : (String, a) -> (col : Collection) -> Collection
(.::) (name, value) col = Cons name value col
delete : (key : String) ->
(col : Collection) ->
{auto prf : IsElement key col} ->
Collection
delete key (Cons key _ rest) {prf = Here} = rest
delete key (Cons name value rest) {prf = (There later)} =
Cons name value (delete key rest)
HeadType : Collection -> Type
HeadType None = Maybe ()
HeadType (Cons {a} _ _ _) = a
data NotEmpty : Collection -> Type where
MkNotEmpty : NotEmpty (Cons k v r)
decompose : (col : Collection) -> {auto prf : NotEmpty col} -> (HeadType col, Collection)
decompose (Cons k v r) {prf = MkNotEmpty} = (v, r)
notInNone : IsElement key None -> Void
notInNone Here impossible
notInNone (There _) impossible
notInTail : (notThere : IsElement key rest -> Void) ->
(notHere : (key = name) -> Void) ->
IsElement key (Cons name value rest) -> Void
notInTail _ notHere Here = notHere Refl
notInTail notThere _ (There later) = notThere later
hasKey : (key : String) -> (col : Collection) -> Dec (IsElement key col)
hasKey key None = No notInNone
hasKey key (Cons name value rest) =
case key `decEq` name of
Yes Refl => Yes Here
No notHere =>
case hasKey key rest of
Yes later => Yes (There later)
No notThere => No (notInTail notThere notHere)
upsert : {a : Type} ->
(name : String) ->
(value : a) ->
(col : Collection) ->
Collection
upsert name value col =
case hasKey name col of
Yes _ => update name value col
No _ => (name, value) .:: col

Related

How to use Kotlin to Write a Y-combinator function?

Can I use Kotlin FP (Lambda, function) to write a Y-combinator function?
Y = λf.(λx.f (x x)) (λx.f (x x))
In JS:
function Y(f) {
return (function (g) {
return g(g);
})(function (g) {
return f(function (x) {
return g(g)(x);
});
});
}
var fact = Y(function (rec) {
return function (n) {
return n == 0 ? 1 : n * rec(n - 1);
};
});
In Coffee:
coffee> Y = (f) -> ((x) -> (x x)) ((x) -> (f ((y) -> ((x x) y))))
[Function]
coffee> fact = Y (f) ->(n) -> if n==0 then 1 else n*f(n-1)
[Function]
coffee> fact(10)
3628800
How can I do this?
In Kotlin, you should introduce an additional interface G, Otherwise you will get the UNCHECKED_CAST warnings, since Kotlin is a statically typed programming language rather than a dynamic language, for example:
typealias X = (Int) -> Int
typealias F = Function1<X, X>
// v-------------v--- let G reference G recursively
interface G : Function1<G, X>
// v--- create a G from lazy blocking
fun G(block: (G) -> X) = object : G {
// v--- delegate call `block(g)` like as `g(g)`
override fun invoke(g: G) = block(g)
}
fun Y(f: F) = (fun(g: G) = g(g))(G { g -> f({ x -> g(g)(x) }) })
val fact = Y({ rec -> { n -> if (n == 0) 1 else n * rec(n - 1) } })
Another version cast a Function1<G, X> to a G, so it should suppress the UNCHECKED_CAST warnings, for example:
typealias X = (Int) -> Int
typealias F = Function1<X, X>
typealias G = Function1<Any, X>
#Suppress("UNCHECKED_CAST")
// v--- cast `g` to `G`.
fun Y(f: F) = (fun(g: Function1<G, X>) = g(g as G))({ g -> f { x -> g(g)(x) } })
val fact = Y({ rec -> { n -> if (n == 0) 1 else n * rec(n - 1) } })

scalamock: how to mock curried method with repeated paramter

I am trying to mock the awscala.dynamodbv2.DynamoDB.putConditionalMethod
How would one define an expects for a method which is curried and includes a repeated parameter:
putConditional(tableName: String, attributes: (String, Any)*)(cond: Seq[(String, aws.model.ExpectedAttributeValue)]): Unit
Here's what I've got working:
(mockClient.putConditional(_: String, _: (String, Any))(_: Seq[(String, ExpectedAttributeValue)]))
.expects("Data-Identity-Partitions",
*,
Seq(
"DatacenterId" -> exp.isNull,
"InstanceId" -> exp.isNull,
"TTL" -> exp.isNull
))
But this:
(mockClient.putConditional(_: String, _: (String, Any))(_: Seq[(String, ExpectedAttributeValue)]))
.expects("Data-Identity-Partitions",
Seq("DatacenterId" -> 1,
"InstanceId" -> 0,
"TTL" -> System.currentTimeMillis()),
Seq(
"DatacenterId" -> exp.isNull,
"InstanceId" -> exp.isNull,
"TTL" -> exp.isNull
))
results in the following compiler error:
[error] AwsPartitionActorSpec.scala:76: type mismatch;
[error] found : Seq[(String, Any)]
[error] required: org.scalamock.matchers.MockParameter[(String, Any)]
[error] Seq[(String, Any)]("DatacenterId" -> 1,
[error] ^
better late than never i suppose, here's my suggestion:
trait testtrait {
def foo(t: String, a: (String, Any) *): Int
}
"foo" should "work" in {
val m = mock[testtrait]
m.foo _ expects where {
case ("foo", Seq(("bar", 42L), ("baz", "mango"))) => true
case _ => false
} returns 5
m.foo("foo", ("bar", 42L), ("baz", "mango")) should be (5)
}

SML - bidirectional infinite and finite sequence interleaving

I have the next declarations of datatype and functions:
datatype direction = Back | Forward
datatype 'a bseq = bNil | bCons of 'a * (direction -> 'a bseq)
fun bHead (bCons (x, _)) = x
| bHead bNil = raise EmptySeq
fun bForward(bCons(_, xf)) = xf Forward
| bForward bNil = raise EmptySeq
fun bBack (bCons (_, xf)) = xf Back
| bBack bNil = raise EmptySeq
fun intbseq k =
let fun go Forward = intbseq (k+1)
| go Back = intbseq (k-1)
in bCons (k, go) end
The next function is written by me for interleaving two sequences like that:
if the first seq is ... ,1,2,3,4,5, ..... and the second is ...,5,6,7,8,9,...
The new sequance of their interleaving is:
... ,3,-1,4,0,5,1,6,2,7,3, ......
Code:
fun binterleaving_aux _ bNil yq = yq
| binterleaving_aux _ xq bNil = xq
| binterleaving_aux firstb (bCons(x,xf)) (bCons(y,yf)) =
bCons(x, fn dir =>
if dir = Forward
then binterleaving_aux true (bCons (y, yf)) (xf dir)
else if firstb
then binterleaving_aux false (yf dir) (xf dir)
else binterleaving_aux false (bCons (y,yf)) (xf dir)));
fun binterleaving bseq1 bseq2 = binterleaving_aux true bseq1 bseq2;
And for that exmaple:
binterleaving (intbseq 5) (intbseq 1);
bForward(it);
bForward(it);
bForward(it);
bForward(it);
bBack(it);
bBack(it);
bBack(it);
bBack(it);
It is working great for 2 infinite sequences.
The problem is when at least one of them is finite.
For exmaple if I do:
binterleaving (bCons(10, fn dir => bCons((9, fn dir => bNil)))) (intbseq 5);
bForward(it);
bForward(it);
bForward(it);
bForward(it);
bBack(it);
bBack(it);
bBack(it);
bBack(it);
If I go back I lose the 10 and 9, and the opposite if firstly I went back, when I move forward I lose them ether.
The result is by the order of the calls:
val it = bCons (10,fn) : int bseq
val it = bCons (5,fn) : int bseq
val it = bCons (9,fn) : int bseq
val it = bCons (6,fn) : int bseq
val it = bCons (7,fn) : int bseq
val it = bCons (6,fn) : int bseq
val it = bCons (5,fn) : int bseq
val it = bCons (4,fn) : int bseq
val it = bCons (3,fn) : int bseq
And the correct result should be:
val it = bCons (10,fn) : int bseq
val it = bCons (5,fn) : int bseq
val it = bCons (9,fn) : int bseq
val it = bCons (6,fn) : int bseq
val it = bCons (7,fn) : int bseq
val it = bCons (6,fn) : int bseq
val it = bCons (9,fn) : int bseq
val it = bCons (5,fn) : int bseq
val it = bCons (10,fn) : int bseq
What are the changes in the code I should do, so that will be the behavior of the function?
The problem is when at least one of them is finite.
binterleaving (bCons(10, fn dir => bCons((9, fn dir => bNil)))) (intbseq 0)
When your finite sequence reduces to bNil, how is it supposed to get back to its original values? The semantics of interleaving a finite sequence with an infinite one seem a little under-defined.
That is, when the finite one ends and the infinite one continues, where is the reference stored along the infinite sequence at which point the finite one starts again in reverse?
Take the example above and evaluate it a few steps (forgive my lazy notation):
binterleaving (bCons(10, fn dir => bCons((9, fn dir => bNil)))) (intbseq 0)
⇒ binterleaving (bCons(10, fn dir => bCons((9, fn dir => bNil))))
(bCons( 0, fn dir => ...intbseq (+/- 1)...))
⇒ binterleaving_aux true (bCons(10, fn dir => bCons((9, fn dir => bNil))))
(bCons( 0, fn dir => ...intbseq (+/- 1)...))
⇒ bCons (9, fn dir =>
if dir = Forward
then binterleaving_aux true (bCons (0, fn dir => ...intbseq (+/- 1)...))
((fn dir => bNil) dir)
else ...)
Evaluating this once by applying Forward to the outermost fn gives:
bCons (9, (fn dir => ...) Forward)
⇒ bCons (9, binterleaving_aux true (bCons (0, fn dir => ...intbseq (+/- 1)...))
((fn dir => bNil) dir))
⇒ bCons (9, binterleaving_aux true (bCons (0, fn dir => ...intbseq (+/- 1)...)) bNil)
⇒ bCons (9, bCons (0, fn dir => ...intbseq (+/- 1)...))
At this point, there is no trace of the finite sequence 9 in any function capable of going backward. Only in the initial return value of binterleaving.
The fix mainly lies in the base case of binterleaving which throws away the finite sequence. Rather, the result of interleaving an empty sequence with a non-empty sequence should be a non-empty sequence that, when reversed, returns whatever the empty sequence was before it got empty (which was possibly also empty, but possibly non-empty).
You can see your bidirectional sequence as a lazy zipper on lists. The book Learn You a Haskell has a chapter on tree zippers that might be worth a read. In this chapter's terminology, you might want a function that returns a "breadcrumb trail". List zippers are conceptually a bit simpler, but sprinkled with the laziness of 'a bseqs, syntactically not so.

Can you use a string for record access in elm?

If I have a string that looks like the name of a field in a record, can I use it to get the data somehow? Something like :
."name".toKey bill
bill.(asSymbol "name")
-
song =
{ title = "foot", artist = "barf", number = "13" }
fieldsILike : List String
fieldsILike =
[ "title", "artist" ]
val song key =
.key song
foo = List.map (val song) fieldsILike --> should be ["foot", "barf"]
No, but you could use a Dict
import Dict exposing (get ,fromList)
song = fromList [ ("title", "foot"), ("artist", "barf") ]
get "title" song -- Just "foot"
get "artist" song -- Just "barf"
get "test" song -- Nothing
Not the way you want it but you can have a function that pattern matches on a string to access a part of a record. You need to be explicit about what it should do in case you give it something invalid.
import Html exposing (..)
type alias Song = { artist : String, number : String, title : String }
song : Song
song =
{ title = "foot", artist = "barf", number = "13" }
fieldsILike : List String
fieldsILike =
[ "title", "artist" ]
k2acc : String -> (Song -> String)
k2acc key =
case key of
"title" -> .title
"artist" -> .artist
"number" -> .number
_ -> always ""
val : Song -> String -> String
val = flip k2acc
-- `flip` was removed in elm 0.19, so you'll need to
-- do something like the following going forward:
-- val song field = (k2acc field) song
foo = List.map (val song) fieldsILike
main = text <| toString foo
I solved this broblem by adding attribute's name and explicit type declaration in AttrValue type:
type alias TypedRecord =
List Attr
type alias Attr =
( String, AttrValue )
type AttrValue
= String String
| Int Int
| Record (List Attr)
So now i can retrieve attributes by "key" (even "key.key" for nested) from my TypedRecord type:
getAttrByKey : String -> TypedRecord -> Maybe Attr
getAttrByKey searchKey item =
-- imitation of searching for attributes likewise in JS Record
let
checkAttrKey =
\k attr ->
first attr == k
in
case String.split "." searchKey of
[ key ] ->
List.head <| List.filter (checkAttrKey key) item
key :: rest ->
case List.head <| List.filter (checkAttrKey key) item of
Just attr ->
case attr of
( _, Record subAttr ) ->
getAttrByKey (String.join "." rest) subAttr
( _, _ ) ->
Nothing
Nothing ->
Nothing
[] ->
Nothing
And conver it to String by checking Attr type and calling respected Elm String module function:
attrToString : Maybe Attr -> String
attrToString is_attr =
case is_attr of
Nothing ->
""
Just attr ->
case second attr of
String value ->
value
Int value ->
String.fromInt value
Record attrs ->
List.map (\a -> Just a) attrs
|> List.map (\a -> attrToString a)
|> String.join " "
These examples for String Int and Record Elm types, but it is also can be extended fo Float Bool and Array.
You can check src/lib/TypedRecord.elm file for another functions and even Json Decoder in my example app repository

Inheritance for functors

Excuse me the lengthy example:
module type MONAD = sig
type ('r, 'a) t
val return : 'a -> ('r, 'a) t
val bind : ('r, 'a) t -> ('a -> ('r, 'b) t) -> ('r, 'b) t
end
module MonadOps (Monad : MONAD) = struct
include Monad
type ('r, 'a) monad = ('r, 'a) t
let run x = x
let return = Monad.return
let bind = Monad.bind
let (>>=) a b = bind a b
let rec foldM f a = function
| [] -> return a
| x::xs -> f a x >>= fun a' -> foldM f a' xs
let whenM p s = if p then s else return ()
let lift f m = perform x <-- m; return (f x)
let join m = perform x <-- m; x
let (>=>) f g = fun x -> f x >>= g
end
module Monad = (MonadOps : functor (M : MONAD) -> sig
type ('a, 'b) monad
val run : ('a, 'b) monad -> ('a, 'b) M.t
val return : 'a -> ('b, 'a) monad
val bind : ('a, 'b) monad -> ('b -> ('a, 'c) monad) -> ('a, 'c) monad
val ( >>= ) :
('a, 'b) monad -> ('b -> ('a, 'c) monad) -> ('a, 'c) monad
val foldM :
('a -> 'b -> ('c, 'a) monad) -> 'a -> 'b list -> ('c, 'a) monad
val whenM : bool -> ('a, unit) monad -> ('a, unit) monad
val lift : ('a -> 'b) -> ('c, 'a) monad -> ('c, 'b) monad
val join : ('a, ('a, 'b) monad) monad -> ('a, 'b) monad
val ( >=> ) :
('a -> ('b, 'c) monad) ->
('c -> ('b, 'd) monad) -> 'a -> ('b, 'd) monad
end)
module type MONAD_PLUS = sig
include MONAD
val mzero : ('r, 'a) t
val mplus : ('r, 'a) t -> ('r, 'a) t -> ('r, 'a) t
end
module MonadPlusOps (MonadPlus : MONAD_PLUS) = struct
include MonadOps (MonadPlus)
let mzero = MonadPlus.mzero
let mplus = MonadPlus.mplus
let fail = mzero
let (++) a b = mplus a b
let guard p = if p then return () else fail
end
Is there a way to have MonadPlus analogous to Monad without excessive signature code duplication? Along the lines of (wrong solution):
module MonadPlus = (MonadPlusOps : functor (M : MONAD_PLUS) -> sig
include module type of MonadPlusOps (M)
with type ('a, 'b) t := ('a, 'b) MonadPlusOps (M).monad
end)
or (does not type-check):
module MonadPlus = (MonadPlusOps : functor (M : MONAD_PLUS) -> sig
include module type of Monad(M)
val mzero : ('a, 'b) monad
(* ... *)
end)
Edit: updated -- better final solution
module type MONAD = sig
type ('s, 'a) t
val return : 'a -> ('s, 'a) t
val bind : ('s, 'a) t -> ('a -> ('s, 'b) t) -> ('s, 'b) t
end
module type MONAD_OPS = sig
type ('s, 'a) monad
include MONAD with type ('s, 'a) t := ('s, 'a) monad
val ( >>= ) :
('s, 'a) monad -> ('a -> ('s, 'b) monad) -> ('s, 'b) monad
val foldM :
('a -> 'b -> ('s, 'a) monad) -> 'a -> 'b list -> ('s, 'a) monad
val whenM : bool -> ('s, unit) monad -> ('s, unit) monad
val lift : ('a -> 'b) -> ('s, 'a) monad -> ('s, 'b) monad
val join : ('s, ('s, 'a) monad) monad -> ('s, 'a) monad
val ( >=> ) :
('a -> ('s, 'b) monad) ->
('b -> ('s, 'c) monad) -> 'a -> ('s, 'c) monad
end
module MonadOps (M : MONAD) = struct
open M
type ('s, 'a) monad = ('s, 'a) t
let run x = x
let (>>=) a b = bind a b
let rec foldM f a = function
| [] -> return a
| x::xs -> f a x >>= fun a' -> foldM f a' xs
let whenM p s = if p then s else return ()
let lift f m = perform x <-- m; return (f x)
let join m = perform x <-- m; x
let (>=>) f g = fun x -> f x >>= g
end
module Monad (M : MONAD) =
sig
include MONAD_OPS
val run : ('s, 'a) monad -> ('s, 'a) M.t
end = struct
include M
include MonadOps(M)
end
module type MONAD_PLUS = sig
include MONAD
val mzero : ('s, 'a) t
val mplus : ('s, 'a) t -> ('s, 'a) t -> ('s, 'a) t
end
module type MONAD_PLUS_OPS = sig
include MONAD_OPS
val mzero : ('s, 'a) monad
val mplus : ('s, 'a) monad -> ('s, 'a) monad -> ('s, 'a) monad
val fail : ('s, 'a) monad
val (++) : ('s, 'a) monad -> ('s, 'a) monad -> ('s, 'a) monad
val guard : bool -> ('s, unit) monad
end
module MonadPlus (M : MONAD_PLUS) :
sig
include MONAD_PLUS_OPS
val run : ('s, 'a) monad -> ('s, 'a) M.t
end = struct
include M
include MonadOps(M)
let fail = mzero
let (++) a b = mplus a b
let guard p = if p then return () else fail
end
I'm not entirely sure what you are trying to achieve, but I would perhaps try to factor it as follows:
module type MONAD =
sig
type ('r, 'a) t
val return : 'a -> ('r, 'a) t
val bind : ('r, 'a) t -> ('a -> ('r, 'b) t) -> ('r, 'b) t
end
module type MONAD_OPS =
sig
type ('a, 'b) monad
val run : ('a, 'b) monad -> ('a, 'b) monad
val (>>=) : ('a, 'b) monad -> ('b -> ('a, 'c) monad) -> ('a, 'c) monad
(* ... *)
end
module MonadOps (Monad : MONAD) :
sig
include MONAD with type ('a ,'b) t := ('a, 'b) Monad.t
include MONAD_OPS with type ('a ,'b) monad = ('a, 'b) Monad.t
end =
struct
include Monad
type ('r, 'a) monad = ('r, 'a) t
let run x = x
let (>>=) = bind
let rec foldM f a = function
| [] -> return a
| x::xs -> f a x >>= fun a' -> foldM f a' xs
(* ... *)
end
module type MONAD_PLUS = sig
include MONAD
val mzero : ('r, 'a) t
val mplus : ('r, 'a) t -> ('r, 'a) t -> ('r, 'a) t
end
module type MONAD_PLUS_OPS =
sig
include MONAD_OPS
val fail : ('r, 'a) monad
val (++) : ('r, 'a) monad -> ('r, 'a) monad -> ('r, 'a) monad
(* ... *)
end
module MonadPlusOps (MonadPlus : MONAD_PLUS) :
sig
include MONAD_PLUS with type ('a ,'b) t := ('a, 'b) Monad.t
include MONAD_PLUS_OPS with type ('a ,'b) monad = ('a, 'b) Monad.t
end =
struct
include MonadPlus
include MonadOps (MonadPlus)
let fail = mzero
let (++) = mplus
(* ... *)
end
As a complement to Andreas' answer, I wished to show that you can use functors to produce signatures. I haven't exactly followed the discussion on which exact level of type abstraction you want, so this code is to be compared with Andreas' version.
module MonadSig = struct
module type S = sig
type ('r, 'a) t
val return : 'a -> ('r, 'a) t
val bind : ('r, 'a) t -> ('a -> ('r, 'b) t) -> ('r, 'b) t
end
end
module MonadOpsSig (M : MonadSig.S) = struct
module type S = sig
type ('a, 'b) monad = ('a, 'b) M.t
val run : ('a, 'b) monad -> ('a, 'b) monad
val (>>=) : ('a, 'b) monad -> ('b -> ('a, 'c) monad) -> ('a, 'c) monad
(* ... *)
end
end
module MonadOps (M : MonadSig.S) : MonadOpsSig(M).S = struct
open M
type ('r, 'a) monad = ('r, 'a) t
let run x = x
let (>>=) = bind
let rec foldM f a = function
| [] -> return a
| x::xs -> f a x >>= fun a' -> foldM f a' xs
(* ... *)
end
module MonadPlusSig = struct
module type S = sig
include MonadSig.S
val mzero : ('r, 'a) t
val mplus : ('r, 'a) t -> ('r, 'a) t -> ('r, 'a) t
end
end
module MonadPlusOpsSig (Monad : MonadPlusSig.S) = struct
module type S = sig
include MonadOpsSig(Monad).S
val fail : ('r, 'a) monad
val (++) : ('r, 'a) monad -> ('r, 'a) monad -> ('r, 'a) monad
(* ... *)
end
end
module MonadPlusOps (M : MonadPlusSig.S) : MonadPlusOpsSig(M).S = struct
include MonadOps(M)
open M
let fail = mzero
let (++) = mplus
(* ... *)
end
The idea is that to provide a signature parametrized on something, you can either embed this signature into a parametrized functor (I'd call this the "functor style"), or define the parameters as abstract (but they're really inputs rather than outputs) and, at use site, equate them with the actual parameters (I'd call this the "mixin style"). I'm not saying the code above is better than Andreas', in fact I'd probably rather use his version, but its interesting to compare them.