How to retrieve the head and tail of a tuple in F# - f#

How do I retrieve the head and tail of a tuple in F#?
For example Conj (a, b), the head is Conj, tail is (a, b).
I want to recursively run buildtree function on each parameters, put the head as Node's element, where is the map in F#?
let rec getparams = map List.head (List.tail getparams);
type Elem = Prop
type Tree = E | T of Elem * Tree * Tree
let rec buildtree vars = function
| E = head vars
| T = buildtree (getparams vars)
After updated:
open System
open Microsoft.FSharp.Reflection
// Learn more about F# at http://fsharp.net
//type Prop = {a: string; b: string}
//let Prop a b = (a, b)
type Op = Prop
type tree = E | T of Op * tree * tree
let tree x y z = (x, y, z)
type binOp = Conj | Disj | Impl
type expr =
| Prop of string
| BinOp of binOp * expr * expr
| Conj of expr * expr
| Disj of expr * expr
| Impl of expr * expr
type Prop = {a: string}
let Prop a = (a)
//type Conj = {a : Prop; b : Prop}
let Conj a b = (a, b)
//type Conj_Int = {a : Prop; b : Prop}
let Conj_Int a b = Conj a b
//type Conj_Elmin1 = {a : Conj}
let Conj_Elmin1 a = fst a
//type Conj_Elmin2 = {a : Conj}
let Conj_Elmin2 a = snd a
//type Impl = {a : Prop; b : Prop}
let Impl a b = (a b)
//type Impl_Int = {assume : Prop; b : Prop}
let Impl_Int assume b = Impl assume b
//type Impl_Elmin = {a :string; b : Impl}
let Impl_Elmin a b = if a = fst b then snd b
type Neg = {a : Prop;}
let Neg a = (a)
//type Double_Neg_Int = {a : Prop;}
let Double_Neg_Int a = Neg(Neg(a))
//type Double_Neg_Elmin = {a : Prop}
let Double_Neg_Elmin a = fst(fst(a))
//type Disj = {a : Prop; b : Prop}
let Disj a b = (a,b)
//type Disj_Int1 = {a : Prop; b : Prop}
let Disj_Int1 a b = (a b)
//type Disj_Int2 = {a : Prop; b : Prop}
let Disj_Int2 a b = (a b)
//type Disj_Elmin1 = {a : Disj}
let Disj_Elmin1 a = fst(a)
//type Disj_Elmin2 = {a : Disj}
let Disj_Elmin2 a = snd(a)
type TupleSplitter = static member splitTuple (a,b,c) = (a,(b,c))
let tupleToList t = if Microsoft.FSharp.Reflection.FSharpType.IsTuple(t.GetType()) then Some (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields t |> Array.toList) else None
let operation x = List.head(List.ofSeq(FSharpValue.GetTupleFields(x)))
let parameters x = List.tail(List.ofSeq(FSharpValue.GetTupleFields(x)))
let rec map f = function | Prop _ as t -> f t | BinOp(op, a, b) -> f(BinOp(op, map f a, map f b))
(*
let rec map f = function
| Prop _ as t -> f t | Conj(a, b) -> f(Conj(map f a, map f b))
| Disj(a, b) -> f(Disj(map f a, map f b))
| Impl(a, b) -> f(Impl(map f a, map f b))
*)
let buildtree vars expr = map (function Prop v -> Map.find v vars | expr -> expr) expr
let t = buildtree(Conj("a","b"))
how to have two type of expression Op*Tree*Tree and Op*Tree?

As Ankur said, you can't get head and tail of a tuple - these operations are designed for processing functional lists that have arbitrary length and cannot be define for tuples that have a length known at compile time. If you want data with arbitrary length, you should probably use tuples and pattern matching (or List.head and List.tail).
If you really need to process tuples dynamically, you can use F# reflection:
open Microsoft.FSharp.Reflection
(1,2,3)
|> FSharpValue.GetTupleFields // Get fields of tuple as an array
|> List.ofSeq // Convert array to a list
|> List.tail // Now you can process list using head/tail
However, note that reflection is generally a bit slow and it should only be used when you need it (i.e. when writing some code that is dynamic and can't be written in any other way).

You seem to be trying to replicate Haskell syntax and semantics in F#. Don't do that. Look at existing ML code and learn how to solve your problem idiomatically. In other words, your question is an XY problem: you're asking the wrong question.
Without knowing what problem you are trying to solve, it is difficult to answer your question but my best guess is:
type Expr =
| Prop of string
| Conj of Expr * Expr
| Disj of Expr * Expr
| Impl of Expr * Expr
let deConj = function
| Conj(a, b) -> a, b
| _ -> invalidArg "expr" "deConj"
Perhaps you want to write a map over your expr type:
let rec map f = function
| Prop _ as t -> f t
| Conj(a, b) -> f(Conj(map f a, map f b))
| Disj(a, b) -> f(Disj(map f a, map f b))
| Impl(a, b) -> f(Impl(map f a, map f b))
Another solution is to rewrite your type to factor out the operators:
type binOp = Conj | Disj | Impl
type expr =
| Prop of string
| BinOp of binOp * expr * expr
let rec map f = function
| Prop _ as t -> f t
| BinOp(op, a, b) -> f(BinOp(op, map f a, map f b))
EDIT
I am not sure what your buildtree function is supposed to do but if it is evaluating expressions then perhaps you want something like this:
let buildtree vars expr =
map (function Proj v -> Map.find v vars | expr -> expr) expr
This will map one expression to another, replacing Proj v with the corresponding expression (i.e. value of the variable v) given by vars.

A tuple is defined as (exp1,exp2, ... ,expn) for example (1,"2",'3').
I can't see this pattern in your code.
If you use (exp1 exp2) it means function application (apply exp2 as first argument to function exp1).
The error you see on your code is because you defined Conj as a function accepting a function as first paramenter and you passed a string ("a") instead of a function.
If your question is how to split a tuple in head and tail you can go for the dynamic approach Tomas just explained, it will work for any n-tuple but you'll lose type information.
Otherwise the strong type solution is simply based on pattern matching:
let splitTuple (a,b,c) = (a,(b,c))
// Usage
let (head,tail) = splitTuple (1,"2",'3')
And if you want to make it work for n-tuples you'll have to define one overload for each n:
type TupleSplitter =
static member splitTuple (a,b,c) = (a,(b,c))
static member splitTuple (a,b,c,d) = (a,(b,c,d))
static member splitTuple (a,b,c,d,e) = (a,(b,c,d,e))
// ... more overloads, as much as you need
// Usage
let (head,tail) = TupleSplitter.splitTuple (1,"2",'3',4.0)
// val tail : string * char * float = ("2", '3', 4.0)
// val head : int = 1

Related

Generic pattern matching in function argument

Let say I have DU of three types and a function which accepts this DU as a parameter:
type A = decimal<p>
type B = decimal<p>
type C = decimal<p>
type ABC = A of A | B of B | C of C
let myfunc (val: ABC) =
match val with
| A v -> ...
| B v -> ...
| C v -> ...
Is there a better way to define a function which accepts DU of only A and B without defining new type explicity?
This is a working example:
type AB = A2 of A | B2 of B
let myfunc2 (val: AB) =
match val with
| A2 v -> ...
| B2 v -> ...
This is how I would like it to be:
let myfunc2 (val: A|B) =
match val with
| A v -> ...
| B v -> ...
The language feature you're asking for is open variants, which exists in OCaml, but not in F#. For instance,
type ABC = A of int | B of int | C of int
type AB = A of int | B of int
let myfunc (x: AB) = ...
is legal in OCaml. This can also be achieved with type-erased DUs (vote for it here!).
DU cases in F# are not actual types, but the base type (ABC) with a tag denoting which union case it is. And since there are no types which exist separately, it follows that they cannot be used as a type constraint.
You can however try and work around this with Choice. The idea is to build single case DUs and then combine them with Choice<T1, T2...>.
type A = A of int
type B = B of int
type C = C of int
type ABC = Choice<A, B, C>
type AB = Choice<A, B>
let matchAb (x: AB) =
match x with
| Choice1Of2 (A a) -> a
| Choice2Of2 (B b) -> b

Extend the W algorithm to containers

I would like to extend the W algorithm to the inference of tuples and lists in F#, a priori, there are only two rules to add, which I did, however, the result is partially bad.
Indeed, if I test a code like these:
test = (8, "Hello", 0.3) -- test :: (TInt, TString, TFloat)
id :: a -> a
id x = x -- id :: a -> a
foo = id 8 -- foo :: TInt
it works, on the other hand, as detailed in the examples below, a code like this one will not work:
makePair = (\x -> (x, x)) -- makePair :: a -> (a, a)
pair = makePair 7
and pair will be inferred as (a, a) instead of (TInt, TInt).
Same for the lists.
I used this paper to write my type checker.
I really don't understand what's jamming.
Here is the minimum functional program used for these examples:
Inference.fs
module Typechecker
type Identifier = string
and Expr =
| Var of Identifier
| Lambda of Identifier * Expr
| Apply of Expr * Expr
| Let of Identifier * Expr * Expr
| Literal of Literal
| Tuple of Expr list
| List of Expr list
and Literal =
| Int of int
| Float of float
| String of string
and Statement =
| Signature of Identifier * Type
| Declaration of Identifier * Expr
and Type =
| TVar of Identifier
| TInt
| TFloat
| TString
| TArrow of Type * Type
| TTuple of Type list
| TList of Type
type Program = Program of Statement list
type Scheme = Scheme of string list * Type
and TypeEnv = Map<Identifier, Scheme>
and Subst = Map<string, Type>
type Env =
{ mutable _functions: Function list }
with
member this.containsFunction name =
this._functions |> List.exists (fun f -> f._name = name)
member this.getFunction name =
this._functions
|> List.find (fun (f: Function) -> f._name = name)
member this.getFunctionType name =
(this.getFunction name)._type
member this.hasFunctionImplementation name =
if this.containsFunction name
then (this.getFunction name)._value.IsSome
else false
member this.getFunctionValue name =
(this.getFunction name)._value.Value
/// Updates the value of a function in the environment
member this.updateFunction_value name value =
(this.getFunction name)._value <- Some value
this
/// Updates the type of a function in the environment
member this.updateFunction_type name ty =
(this.getFunction name)._type <- ty
this
member this.addFunction name ty value =
{ _functions =
List.append
this._functions
[{ _name = name;
_type = ty;
_value = value }] }
and Function =
{ _name: Identifier;
mutable _type: Type;
mutable _value: Expr option }
and DataType =
{ _name: Identifier;
_isAlias: bool;
_constructors: Ctor list option
_alias: Type option }
and Ctor = Term of Identifier | Product of Type list
let newEnv = { _functions = [] }
module Type =
let rec ftv = function
| TInt -> Set.empty
| TFloat -> Set.empty
| TString -> Set.empty
| TVar name -> Set.singleton name
| TArrow(t1, t2) -> Set.union (ftv t1) (ftv t2)
| TTuple ts -> List.fold (fun acc t -> Set.union acc (ftv t)) Set.empty ts
| TList t -> Set.singleton (toString t)
and apply s t =
match t with
| TVar name ->
match Map.tryFind name s with
| Some t -> t
| None -> TVar name
| TArrow (t1, t2) ->
TArrow(apply s t1, apply s t2)
| TInt | TFloat | TString -> t
| _ -> t
and parens s =
sprintf "(%s)" s
and braces s =
sprintf "{ %s }" s
and toString t =
let rec parenType t' =
match t' with
| TArrow(_, _) -> parens (toString t')
| _ -> toString t'
match t with
| TVar name -> name
| TInt -> "Integer"
| TFloat -> "Float"
| TString -> "String"
| TArrow(t1, t2) ->
(parenType t1) + " -> " + (toString t2)
| TTuple ts -> sprintf "(%s)" (System.String.Join(", ", List.map toString ts))
| TList t -> sprintf "[%s]" (toString t)
module Scheme =
let rec ftv (scheme: Scheme) =
match scheme with
| Scheme(variables, t) ->
Set.difference(Type.ftv t) (Set.ofList variables)
and apply (s: Subst) (scheme: Scheme) =
match scheme with
| Scheme(variables, t) ->
let newSubst = List.foldBack (fun key currentSubst -> Map.remove key currentSubst) variables s
let newType = Type.apply newSubst t
Scheme(variables, newType)
module TypeEnv =
let rec remove (env: TypeEnv) (var: Identifier) =
Map.remove var env
and ftv (typeEnv: TypeEnv) =
Seq.foldBack (fun (KeyValue(_, v)) state ->
Set.union state (Scheme.ftv v)) typeEnv Set.empty
and apply (s: Subst) (env: TypeEnv) =
Map.map (fun _ value -> Scheme.apply s value) env
module Subst =
let compose s1 s2 =
Map.union (Map.map (fun _ (v: Type) -> Type.apply s1 v) s2) s1
let rec generalize (env: TypeEnv) (t: Type) =
let variables =
Set.difference (Type.ftv t) (TypeEnv.ftv env)
|> Seq.toList
Scheme(variables, t)
and private currentId = ref 'a'
and nextId () =
let id = !currentId
currentId := (char ((int !currentId) + 1))
id
and resetId () = currentId := 'a'
and newTyVar () =
TVar(sprintf "%c" (nextId ()))
and instantiate (ts: Scheme) =
match ts with
| Scheme(variables, t) ->
let nvars = variables |> List.map (fun name -> newTyVar ())
let s = List.zip variables nvars |> Map.ofList
Type.apply s t
and varBind a t =
match t with
| TVar name when name = a -> Map.empty
| _ when Set.contains a (Type.ftv t) ->
failwithf "Occur check fails: `%s` vs `%s`" a (Type.toString t)
| _ -> Map.singleton a t
and unify (t1: Type) (t2: Type) : Subst =
match t1, t2 with
| TVar a, t | t, TVar a -> varBind a t
| TInt, TInt -> Map.empty
| TFloat, TFloat -> Map.empty
| TString, TString -> Map.empty
| TArrow(l, r), TArrow(l', r') ->
let s1 = unify l l'
let s2 = unify (Type.apply s1 r) (Type.apply s1 r')
Subst.compose s2 s1
| TTuple ts, TTuple ts' ->
if ts.Length <> ts'.Length
then failwithf "Types do not unify: `%s` vs `%s`" (Type.toString t1) (Type.toString t2)
else List.fold Subst.compose Map.empty (List.map2 unify ts ts')
| TList t, TList t' ->
unify t t'
| _ -> failwithf "Types do not unify: `%s` vs `%s`" (Type.toString t1) (Type.toString t2)
and tiLit = function
| Literal.Int _ -> Type.TInt
| Literal.Float _ -> Type.TFloat
| Literal.String _ -> Type.TString
and tiExpr (env: TypeEnv) (exp: Expr) : Subst * Type =
match exp with
| Var name ->
match Map.tryFind name env with
| Some sigma ->
let t = instantiate sigma
(Map.empty, t)
| None -> failwithf "Unbound variable: `%s`" name
| Literal lit -> (Map.empty, tiLit lit)
| Let(id, expr, in') ->
let s1, t1 = tiExpr env expr
let env1 = TypeEnv.remove env id
let scheme = generalize (TypeEnv.apply s1 env) t1
let env2 = Map.add id scheme env1
let s2, t2 = tiExpr (TypeEnv.apply s1 env2) in'
(Subst.compose s2 s1, t2)
| Lambda(id, value) ->
let tv = newTyVar ()
let env1 = TypeEnv.remove env id
let env2 = Map.union env1 (Map.singleton id (Scheme([], tv)))
let s1, t1 = tiExpr env2 value
(s1, TArrow(Type.apply s1 tv, t1))
| Tuple values ->
(Map.empty, TTuple(List.map (fun v -> snd (tiExpr env v)) values))
| List ts ->
let tv = newTyVar ()
if ts.IsEmpty
then (Map.empty, TList tv)
else
let _, t1 = tiExpr env ts.[0]
if List.forall (fun t -> snd (tiExpr env t) = t1) ts
then (Map.empty, TList t1)
else failwith "Not all items in the list are of the same type"
| Apply(e1, e2) ->
let s1, t1 = tiExpr env e1
let s2, t2 = tiExpr (TypeEnv.apply s1 env) e2
let tv = newTyVar ()
let s3 = unify (Type.apply s2 t1) (TArrow(t2, tv))
(Subst.compose (Subst.compose s3 s2) s1, Type.apply s3 tv)
and expression_typeInference env exp =
let s, t = tiExpr env exp
Type.apply s t
and updateExprEnv (env: Env) =
let mutable env' = Map.empty
List.iter
(fun (f: Function) ->
env' <- env'.Add(f._name, Scheme([f._name], f._type))
) env._functions
env'
let rec public statement_typecheck (env: Env) stmt =
let exprEnv = updateExprEnv env
match stmt with
| Signature(id, dastType) ->
typecheck_signature env id dastType
| Declaration(id, value) ->
typecheck_decl env id value exprEnv
and private typecheck_signature (env: Env) id ty =
if env.hasFunctionImplementation id
then failwithf "The type of a function cannot be defined after its implementation (`%s`)" id
else env.addFunction id ty None
and private typecheck_decl (env: Env) id value exprEnv =
let _, t_exp = tiExpr exprEnv value
if env.containsFunction id
then if env.hasFunctionImplementation id
then failwithf "Already declared function: `%s`" id
else
let t_sgn = (env.getFunction id)._type
let unapp = try (Type.apply ((unify t_sgn t_exp)) t_exp)
|> Ok with exn -> failwithf "%s" exn.Message
if match unapp with Result.Ok _ -> true
then env.updateFunction_value id value
else failwithf "The signature of `%s` is different than the type of its value\n (`%s` vs `%s`)"
id (Type.toString t_sgn) (Type.toString t_exp)
else env.addFunction id t_exp (Some value)
let typecheck_document (document: Program) =
let mutable docenv = newEnv
match document with Program stmts ->
List.iter (fun stmt -> docenv <- statement_typecheck docenv stmt) stmts
docenv
Main.fs
module Main
open Inference
[<EntryPoint>]
let main _ =
let test1 =
[Declaration("makePair", Lambda("x", Tuple([Var "x"; Var "x"]))); // makePair = (\x -> (x, x))
Declaration("pair", Apply(Var "makePair", Literal (Int 7)))] // pair = makePair 7
let infer1 = typecheck_document (Program test1)
printfn "Env1: %A" infer1
let test2 =
[Signature("id", TArrow(TVar "a", TVar "a")); // id :: a -> a
Declaration("id", Lambda("x", Var "x")) // id x = x
Declaration("foo", Apply(Var "id", Literal (Int 7)))] // foo = id 7
let infer2 = typecheck_document (Program test2)
printfn "Env2: %A" infer2
0
Here is the output:
Env1: {_functions =
[{_name = "makePair";
_type = TArrow (TVar "a",TTuple [TVar "a"; TVar "a"]);
_value = Some (Lambda ("x",Tuple [Var "x"; Var "x"]));};
{_name = "pair";
_type = TTuple [TVar "a"; TVar "a"];
_value = Some (Apply (Var "makePair",Literal (Int 7)));}];}
Env2: {_functions =
[{_name = "id";
_type = TArrow (TVar "a",TVar "a");
_value = Some (Lambda ("x",Var "x"));};
{_name = "test";
_type = TInt;
_value = Some (Apply (Var "id",Literal (Int 7)));}];}
So we can see that the inference works correctly for the first test, but not for the second (as shown above).
I would like to solve and understand this bug, and I thank you in advance for your help.
As far as I read in your code, it seems like you're missing a branch in apply.
Indeed, when t is a Tuple [], you're basically only returning it...which well will of course not work. :)
I suggest adding one branch to the match, with t as a Tuple types, with a map (\t -> apply s t) types (sorry, I don't know much F# syntax).
Hope it helps. :)

Railway Oriented Programming and partial application

I like using ROP when I have to deal with IO/Parsing strings/...
However let's say that I have a function taking 2 parameters. How can you do clean/readable partial application when your 2 parameters are already a Result<'a,'b> (not necessary same 'a, 'b)?
For now, what I do is that I use tuple to pass parameters and use the function below to get a Result of a tuple so I can then bind my function with this "tuple-parameter".
/// Transform a tuple of Result in a Result of tuple
let tupleAllResult x =
match (fst x, snd x) with
| Result.Ok a, Result.Ok b -> (a,b) |> Result.Ok
| Result.Ok a, Result.Error b -> b |> Result.Error
| Result.Error a, _ -> a |> Result.Error
let f (a: 'T, b: 'U) = // something
(A, B) |> tupleAllResult
|> (Result.bind f)
Any good idea?
Here what I wrote, which works but might not be the most elegant
let resultFunc (f: Result<('a -> Result<'b, 'c>), 'd>) a =
match f with
| Result.Ok g -> (g a) |> Result.Ok |> Result.flatten
| Result.Error e -> e |> Result.Error |> Result.flatten
I am not seeing partial application in your example, a concept related to currying and argument passing -- that's why I am assuming that you are after the monadic apply, in that you want to transform a function wrapped as a Result value into a function that takes a Result and returns another Result.
let (.>>.) aR bR = // This is "tupleAllResult" under a different name
match aR, bR with
| Ok a, Ok b -> Ok(a, b)
| Error e, _ | _, Error e -> Error e
// val ( .>>. ) : aR:Result<'a,'b> -> bR:Result<'c,'b> -> Result<('a * 'c),'b>
let (<*>) fR xR = // This is another name for "apply"
(fR .>>. xR) |> Result.map (fun (f, x) -> f x)
// val ( <*> ) : fR:Result<('a -> 'b),'c> -> xR:Result<'a,'c> -> Result<'b,'c>
The difference to what you have in your question is map instead of bind in the last line.
Now you can start to lift functions into the Result world:
let lift2 f xR yR =
Ok f <*> xR <*> yR
// val lift2 :
// f:('a -> 'b -> 'c) -> xR:Result<'a,'d> -> yR:Result<'b,'d> -> Result<'c,'d>
let res : Result<_,unit> = lift2 (+) (Ok 1) (Ok 2)
// val res : Result<int,unit> = Ok 3

Could the dictionary of operations be eliminated from this generalized Set definition?

I am trying to generalize the concept of a Set in F#. Among other things I want to define sets using inequalities. This would help me simplifying some sections of my code. So I created a type MySet as follows:
type Comparison = | GE
| GT
| LE
| LT
| EQ
type ComparisonOps<'t> = { gt: 't->'t->bool
ge: 't->'t->bool
eq: 't->'t->bool
le: 't->'t->bool
lt: 't->'t->bool }
type MySet<'t when 't : comparison> =
| List of list<'t>
| Sequence of seq<'t>
| Array of 't []
| String of string
| Set of Set<'t>
| Compare of (ComparisonOps<'t>*Comparison*'t)
Note: I intend to make MySet recursive later, allowing for unions and intersections, but for the purposes of this question this is not necessary.
The whole point of the new MySet type is to allow checking if elements of different types belong to sets of different cases. This is implemented by this function:
let elementOf<'t when 't : comparison> (st: MySet<'t>) (x: 't) : bool =
match st with
| List xs -> List.contains x xs
| Sequence s -> Seq.contains x s
| Array a -> Array.contains x a
| Set st -> Set.contains x st
| String str -> match box str with
| :? string as s -> match box x with
| :? string as z -> s.Contains z
| _ -> false
| _ -> false
| Compare (comp: ComparisonOps<'t>*Comparison*'t) ->
let compOps, cmp, y = comp
match cmp with
| GT -> compOps.gt x y
| GE -> compOps.ge x y
| EQ -> compOps.eq x y
| LE -> compOps.le x y
| LT -> compOps.lt x y
Note: I also plan to generalize elementOf allowing for function application, but again this is not needed here.
The function works:
let myStringSet = MySet.String("XYZ")
let strb = "X" |> elementOf<string> myStringSet
printfn "strb = %b" strb // strb = true
let myListSet = MySet.List([0..10])
let listb = 5 |> elementOf<int> myListSet
printfn "listb = %b" listb // listb = true
let myCompSet = MySet.Compare((ComparisonFloat, GT, 0.0))
let compb = -1.0 |> elementOf<float> myCompSet
printfn "compb = %b" compb // compb = false
let myCompSet2 = MySet.Compare((ComparisonString, LT, "XYZ"))
let compb2 = "XA" |> elementOf<string> myCompSet2
printfn "compb2 = %b" compb2 // compb2 = true
That is great, but I wonder if I really need to create the dictionary of operations ComparisonOps, since operations like < are polymorphic on the types int, float and string anyway.
Eliminating ComparisonOps could considerably simplify the code. Is that possible?
As Fyodor Soikin notes, it sounds like maybe what you want is to define a set as all elements satisfying a predicate:
type MySet<'t> = | MySet of ('t -> bool)
Then set operations are easy to define:
let intersect (MySet p1) (MySet p2) = MySet(fun t -> p1 t && p2 t)
And all of your specific constructors can just be turned into simple functions:
let ofList l = MySet(fun t -> List.contains t l)
let lt x = MySet(fun t -> t < x)

How do sequence expressions and polymorphic recursion play together?

This project really is a source of questions for me.
I already learned about polymorphic recursion and I understand why it is a special case and therefore F# needs full type annotations.
For regular functions I might need some fiddeling but usually get it right. Now I'm trying to adapt a (working) basic toSeq to a more specialized finger tree, but can't.
My feeling is that the use of the computation expression has something to do with it. This is the condensed working version:
module ThisWorks =
module Node =
type Node<'a> =
| Node2 of 'a * 'a
| Node3 of 'a * 'a * 'a
let toList = function
| Node2(a, b) -> [a; b]
| Node3(a, b, c) -> [a; b; c]
module Digit =
type Digit<'a> =
| One of 'a
| Two of 'a * 'a
| Three of 'a * 'a * 'a
| Four of 'a * 'a * 'a * 'a
let toList = function
| One a -> [a]
| Two(a, b) -> [a; b]
| Three(a, b, c) -> [a; b; c]
| Four(a, b, c, d) -> [a; b; c; d]
module FingerTree =
open Node
open Digit
type FingerTree<'a> =
| Empty
| Single of 'a
| Deep of Digit<'a> * Lazy<FingerTree<Node<'a>>> * Digit<'a>
let rec toSeq<'a> (tree:FingerTree<'a>) : seq<'a> = seq {
match tree with
| Single single ->
yield single
| Deep(prefix, Lazy deeper, suffix) ->
yield! prefix |> Digit.toList
yield! deeper |> toSeq |> Seq.collect Node.toList
yield! suffix |> Digit.toList
| Empty -> ()
}
The one I don't manage to get to compile is this:
module ThisDoesnt =
module Monoids =
type IMonoid<'m> =
abstract Zero:'m
abstract Plus:'m -> 'm
type IMeasured<'m when 'm :> IMonoid<'m>> =
abstract Measure:'m
type Size(value) =
new() = Size 0
member __.Value = value
interface IMonoid<Size> with
member __.Zero = Size()
member __.Plus rhs = Size(value + rhs.Value)
type Value<'a> =
| Value of 'a
interface IMeasured<Size> with
member __.Measure = Size 1
open Monoids
module Node =
type Node<'m, 'a when 'm :> IMonoid<'m>> =
| Node2 of 'm * 'a * 'a
| Node3 of 'm * 'a * 'a * 'a
let toList = function
| Node2(_, a, b) -> [a; b]
| Node3(_, a, b, c) -> [a; b; c]
module Digit =
type Digit<'m, 'a when 'm :> IMonoid<'m>> =
| One of 'a
| Two of 'a * 'a
| Three of 'a * 'a * 'a
| Four of 'a * 'a * 'a * 'a
let toList = function
| One a -> [a]
| Two(a, b) -> [a; b]
| Three(a, b, c) -> [a; b; c]
| Four(a, b, c, d) -> [a; b; c; d]
module FingerTree =
open Node
open Digit
type FingerTree<'m, 'a when 'm :> IMonoid<'m>> =
| Empty
| Single of 'a
| Deep of 'm * Digit<'m, 'a> * Lazy<FingerTree<'m, Node<'m, 'a>>> * Digit<'m, 'a>
let unpack (Value v) = v
let rec toSeq<'a> (tree:FingerTree<Size, Value<'a>>) : seq<'a> = seq {
match tree with
| Single(Value single) ->
yield single
| Deep(_, prefix, Lazy deeper, suffix) ->
yield! prefix |> Digit.toList |> List.map unpack
#if ITERATE
for (Value deep) in toSeq deeper do
^^^^^
yield deep
#else
yield! deeper |> toSeq |> Seq.collect (Node.toList >> List.map unpack)
^^^^^
#endif
yield! suffix |> Digit.toList |> List.map unpack
| Empty -> ()
}
The error message I get says
Error Type mismatch. Expecting a
FingerTree<Size,Node<Size,Value<'a>>> -> 'b
but given a
FingerTree<Size,Value<'c>> -> seq<'c>
The type 'Node<Size,Value<'a>>' does not match the type 'Value<'b>'
and the squiggles underline the recursive call of toSeq.
I know that the “deeper” type is encapsulated in a Node and in the working code I just unpack it afterwards. But here the compiler trips already before I get the chance to unpack. Trying a for (Value deep) in toSeq deeper do yield deep has the same problem.
I already have a way out, namely to use the toSeq of the “base” Tree and Seq.map unpack afterwards. Not true, trying that yields a very similar error message.
I'm curious what makes this code break and how it could be fixed.
The compiler's error message seems clear to me: toSeq is applicable only to values of type FingerTree<Size, Value<'a>> for some 'a, but you're trying to call it on a value of type FingerTree<Size,Node<Size,Value<'a>>> instead, which is not compatible. There's nothing specific to polymorphic recursion or sequence expressions, these types just don't match.
Instead, it seems like it would be much simpler to make toSeq more generic by taking an input of type FingerTree<Size, 'a> (without any reference to Value), which would enable the recursive call you want. Then you can easily derive the more specific function you actually want by composing the more general toSeq with Seq.map unpack.

Resources