I'm wondering, is it possible to have records whose values depend on each other, without being recursively defined in terms of types?
Basically, I have a record type that looks like this:
record SomeRec (a : Set) : Set where
method1 : a -> a -> Foo
method2 : a -> a -> Bar
...
method n : a -> a -> Baz
where n is large enough that I want to actually bundle all of these up as records, instead of just having a bunch of mutually recursive functions. (Also, I'm using SomeRec as an instance argument).
Notably, SomeRec is not recursive: none of its fields types refer to SomeRec.
Right now, I have a Description D, where FD : Set -> Set is the functor described by D. What I'm trying to show is the following:
muRec : ( FRec : (a : Set) -> SomeRec a -> SomeRec (FD a) ) -> SomeRec (mu D)
That is, if I can take a (SomeRec a) to a (SomeRec (F a)), then I can tie the knot recursively and show the instance for the whole thing.
In principle this can be done: each recursive call to a field of SomeRec would happen with strictly smaller arguments. If I were to just have 10 mutually recursive methods, it would work. But if I try to build the record, I end up passing muRec to FRec without its arguments decreasing.
So, what I'm wondering is: is there a way to show that this is well-founded? Can induction or coinduction help, even though the record type is not inductive or coinductive?
My current approach is to make a size-indexed version of D, and build SomeRec for size n out of SomeRec for size < n. But this is dramatically increasing the complexity of what I'm doing, and I'm having to use Brouwer ordinals to get size bounds on what I'm doing. So I'm wondering if there's a better way.
Thanks!
The FRec : (a : Set) -> SomeRec a -> SomeRec (FD a) argument is asking for too much. It shouldn't be asking for a full SomeRec a, but rather only for the results of calling those methods on the subterms of some x : FD a.
If the methods are really simply typed you could instead define
record Results : Set where
field1 : Foo
field2 : Bar
...
fieldn : Baz
Then have muRec have a type like
muRec : (FRec : (a : Set) (x : FD a) (y : FD a)
(IH : (t1 t2 : a) -> In t1 x -> In t2 y -> Results)
-> Results)
-> mu D -> mu D -> Results
The type of IH might need to be adjusted depending on your recursion pattern.
And In : {a : Set} -> a -> FD a -> Set is some predicate you should be able to define by using D, which should relate subtrees to their parent.
Related
Upon covering the predefined datatypes in f# (i.e lists) and how to sum elements of a list or a sequence, I'm trying to learn how I can work with user defined datatypes. Say I create a data type, call it list1:
type list1 =
A
| B of int * list1
Where:
A stands for an empty list
B builds a new list by adding an int in front of another list
so 1,2,3,4, will be represented with the list1 value:
B(1, B(2, B(3, B(4, A))))
From the wikibook I learned that with a list I can sum the elements by doing:
let List.sum [1; 2; 3; 4]
But how do I go about summing the elements of a user defined datatype? Any hints would be greatly appreciated.
Edit: I'm able to take advantage of the match operator:
let rec sumit (l: ilist) : int =
match l with
| (B(x1, A)) -> x1
| (B(x1, B(x2, A))) -> (x1+x2)
sumit (B(3, B(4, A)))
I get:
val it : int = 7
How can I make it so that if I have more than 2 ints it still sums the elemets (i.e. (B(3, B(4, B(5, A)))) gets 12?
One good general approach to questions like this is to write out your algorithm in word form or pseudocode form, then once you've figured out your algorithm, convert it to F#. In this case where you want to sum the lists, that would look like this:
The first step in figuring out an algorithm is to carefully define the specifications of the problem. I want an algorithm to sum my custom list type. What exactly does that mean? Or, to be more specific, what exactly does that mean for the two different kinds of values (A and B) that my custom list type can have? Well, let's look at them one at a time. If a list is of type A, then that represents an empty list, so I need to decide what the sum of an empty list should be. The most sensible value for the sum of an empty list is 0, so the rule is "I the list is of type A, then the sum is 0". Now, if the list is of type B, then what does the sum of that list mean? Well, the sum of a list of type B would be its int value, plus the sum of the sublist.
So now we have a "sum" rule for each of the two types that list1 can have. If A, the sum is 0. If B, the sum is (value + sum of sublist). And that rule translates almost verbatim into F# code!
let rec sum (lst : list1) =
match lst with
| A -> 0
| B (value, sublist) -> value + sum sublist
A couple things I want to note about this code. First, one thing you may or may not have seen before (since you seem to be an F# beginner) is the rec keyword. This is required when you're writing a recursive function: due to internal details in how the F# parser is implemented, if a function is going to call itself, you have to declare that ahead of time when you declare the function's name and parameters. Second, this is not the best way to write a sum function, because it is not actually tail-recursive, which means that it might throw a StackOverflowException if you try to sum a really, really long list. At this point in your learning F# you maybe shouldn't worry about that just yet, but eventually you will learn a useful technique for turning a non-tail-recursive function into a tail-recursive one. It involves adding an extra parameter usually called an "accumulator" (and sometimes spelled acc for short), and a properly tail-recursive version of the above sum function would have looked like this:
let sum (lst : list1) =
let rec tailRecursiveSum (acc : int) (lst : list1) =
match lst with
| A -> acc
| B (value, sublist) -> tailRecursiveSum (acc + value) sublist
tailRecursiveSum 0 lst
If you're already at the point where you can understand this, great! If you're not at that point yet, bookmark this answer and come back to it once you've studied tail recursion, because this technique (turning a non-tail-recursive function into a tail-recursive one with the use of an inner function and an accumulator parameter) is a very valuable one that has all sorts of applications in F# programming.
Besides tail-recursion, generic programming may be a concept of importance for the functional learner. Why go to the trouble of creating a custom data type, if it only can hold integer values?
The sum of all elements of a list can be abstracted as the repeated application of the addition operator to all elements of the list and an accumulator primed with an initial state. This can be generalized as a functional fold:
type 'a list1 = A | B of 'a * 'a list1
let fold folder (state : 'State) list =
let rec loop s = function
| A -> s
| B(x : 'T, xs) -> loop (folder s x) xs
loop state list
// val fold :
// folder:('State -> 'T -> 'State) -> state:'State -> list:'T list1 -> 'State
B(1, B(2, B(3, B(4, A))))
|> fold (+) 0
// val it : int = 10
Making also the sum function generic needs a little black magic called statically resolved type parameters. The signature isn't pretty, it essentially tells you that it expects the (+) operator on a type to successfully compile.
let inline sum xs = fold (+) Unchecked.defaultof<_> xs
// val inline sum :
// xs: ^a list1 -> ^b
// when ( ^b or ^a) : (static member ( + ) : ^b * ^a -> ^b)
B(1, B(2, B(3, B(4, A))))
|> sum
// val it : int = 10
In Agda 2.5.1.1 on Windows, after the code below is loaded (it corresponds to the tutorial https://github.com/k0001/tut-agda/blob/master/SetsParametric.agda), the C-c C-d type-checking does find the type List₁ _A_2 _B_3 for the [] expression, but no reasonable type for any more structured expression like true ∷ [] , just underscore and number is returned, like _5 . Any ideas what the reason could be, please?
The previous exercises of the tutorial work well.
module Sets.Parametric where
open import Sets.Enumerated using (Bool; true; false; ⊤; tt)
data List₁ (A B : Set) : Set
data List₂ (A B : Set) : Set
data List₁ (A B : Set) where
[] : List₁ A B
_∷_ : A → List₂ A B → List₁ A B
data List₂ (A B : Set) where
_∷_ : B → List₁ A B → List₂ A B
Non-overloaded constructors are inferrable and hence the type of [] is inferred, but overloaded constructors are only checkable, so you can't infer the type true ∷ [] — only check it against List₂ Bool A.
Otherwise type-directed resolution for overloaded constructors would be too complicated. E.g. the type of the second argument of _∷_ could depend on its first argument, then figuring out whether _∷_ belongs to List₁ or List₂ would require solving two possibly non-trivial unification problems (one for List₁ and one for List₂) which likely will be postponed and sit in memory until it's clear which _∷_ the user means. Agda already generates lots of metavariables and I don't see any reason to increase this number and complicate type checking to incorporate this not super useful feature.
Can someone explain please why in F# type-inference seems to work differently (or some other aspect I don't understand?) between class methods and functions.
Imagine the following (simplified):
type Node<'T> = Node2 of 'T * 'T
type Digit<'T> = One of 'T | Two of 'T * 'T
type Tree<'T> =
| Empty
| Single of 'T
| Deep of prefix : Digit<'T> * deeper : Tree<Node<'T>>
with
static member Add (value : 'T) (tree : Tree<'T>) : Tree<'T> =
match tree with
| Empty -> Single value
| Single a -> Deep (One value, Empty)
| Deep (One a, deeper) -> Deep (Two (value, a), deeper)
| Deep (Two (b, a), deeper) -> Deep (One value, deeper |> Tree.Add (Node2 (b, a)))
let rec add (value : 'T) (tree : Tree<'T>) : Tree<'T> =
match tree with
| Empty -> Single value
| Single a -> Deep (One value, Empty)
| Deep (One a, deeper) -> Deep (Two (value, a), deeper)
| Deep (Two (b, a), deeper) -> Deep (One value, deeper |> add (Node2 (b, a)))
Note that static method Add and function add have identical implementation and both call itself recursively.
Yet former compiles fine but latter report an error:
Type mismatch. Expecting a
Tree<Node<'T>> -> Tree<Node<'T>>
but given a
Tree<'T> -> Tree<'T>
The resulting type would be infinite when unifying ''T' and 'Node<'T>'
In the free floating function add, the generic type parameter belongs to the function itself (add<'T>).
However, in the static member function, the type parameter actually belongs to the class (Tree<'T>).
Why does this matter? Because when you refer to the function itself, the compiler assumes that the type parameter is unchanged unless otherwise specified. It won't guess a different one, because that could hide a huge category of type mismatch errors.
However, it doesn't make the same assumption for the type to which the function belongs.
If you check the parameters, the call to add is assumed to be a call to add<'T>, which causes an infinite generic recursion and doesn't compile.
But, the call to Tree.Add is inferred to be a call to Tree<Node<'T>>.Add, not to Tree<'T>.Add. It's a different function call altogether.
If you explicitly annotate the type:
static member Add (value : 'T) (tree : Tree<'T>) : Tree<'T> =
// ...
| Deep (Two (b, a), deeper) -> Deep (One value, deeper |> Tree<'T>.Add (Node2 (b, a)))
you will get the exact same type mismatch / infinite type error as on the free function.
Likewise you get the error if you make it an instance member and refer to the same instance:
member this.Add (value : 'T) (tree : Tree<'T>) : Tree<'T> =
// ...
| Deep (Two (b, a), deeper) -> Deep (One value, deeper |> this.Add (Node2 (b, a)))
Vice-versa, you can make the free function compile by annotating the type parameter, so that the compiler will not assume "it's the same symbol, so must refer to the same value":
let rec add<'T> (value : 'T) (tree : Tree<'T>) : Tree<'T> =
// ...
| Deep (Two (b, a), deeper) -> Deep (One value, deeper |> add (Node2 (b, a)))
I wanted to implement this statement in agda ;
A dedekind cut is a pair (L, U) of mere predicates L : Q -> Set and R : Q -> Set which is
1) inhibited : exists (q : Q) . L(q) ^ exists (r : Q) . U(r)
I have tried in this way,
record cut : Set where
field
L : Q -> Set
R : Q -> Set
inhibited : exists (q : Q) . L(q) ^ exists (r : Q) . U(r)
but this is not working. I want to write this and i am struck please help. And also what is the difference between 1)data R : Set and record R : Set and 2) data R : Set and data R : Q -> Set
I don't know about defining the whole of the dedekind cut, though I did find your definition on page 369 of Homotopy Type Theory: Univalent Foundations of Mathematics.
Here is the syntax for defining what you asked about in your question in two forms, one using the standard library and one expanded out to show what it is doing.
open import Data.Product using (∃)
record Cut (Q : Set) : Set₁ where
field
L U : Q → Set -- The two predicates
L-inhabited : ∃ L
U-inhabited : ∃ U
If we manually expand the definition of ∃ (exists) we have:
record Cut′ (Q : Set) : Set₁ where
field
L U : Q → Set -- The two predicates
q r : Q -- Witnesses
Lq : L q -- Witness satisfies predicate
Ur : U r -- Witness satisfies predicate
Note that the record has type Set₁ due to the types of fields L and U.
Regarding your question about records and inductively defined data types, there are lots of differences. You might want to start with the wiki and ask more specific questions if you get stuck somewhere: Data, Records
I have a general function that takes a lot of parameters
f : a -> b -> c -> d -> e -> f
I want to provide specialized functions that only take the last two parameters, but provide some fixed values for the first three.
g : d -> e -> f
h : d -> e -> f
Their implementation is something like the following
g = f someA someB someC
h = f someA' someB' someC'
This is all great of course, but when it comes to invoking those functions from C# it's a problem because their types don't get "prettified". Instead I get a bunch of nested FSharpFuncs.
I can avoid this problem by defining my functions like
g d e = f someA someB someC d e
h d e = f someA' someB' someC' d e
But this seems like a really simple, mechanical transformation so I'm wondering if there's an automated way to get the same result. Perhaps some attribute I can attach to them?
Technically speaking, the first and second options of how to write your g and h are not exactly the same. In the first case, f is applied to three arguments and the resulting new function is stored as an object in the value g.
Whereas in the second case, the function f is called with all 5 arguments every time with the values of someA, someB and someC being passed at the time of calling g.
For most cases, this distinction is not really relevant, but it becomes important when you want to cache some parts of your computation.
Long story short: The transformation has a slight semantic difference and therefore cannot really be done automatically. Just add the arguments to the new g and h.