Why idris2 can't proof that div 1 2 < 1 = True? - proof

When I write:
div_1_2_lower_than_1 : div (S Z) 2 < (S Z) = True
div_1_2_lower_than_1 = Refl
I get error:
While processing right hand side of div_1_2_lower_than_1. Can't solve constraint
between: True and compare (1 `div` 2) 1 == LT.
Meanwhile when I write:
minus_1_2_lower_than_1 : minus (S Z) 2 < (S Z) = True
minus_1_2_lower_than_1 = Refl
Everything is fine.
Why?
Updated:
I used Data.Nat and natDiv for some reason fundamentally not validated.
div_1_2_eq_0 : div (S Z) (S (S Z)) = 0
div_1_2_eq_0 = Refl
Error:
While processing right hand side of div_1_2_eq_0. Can't solve constraint between: 0 and divNat 1 2.

I think this is right. The implementation of div isn't exported so the type checker can't reduce it
export partial
divNat : Nat -> Nat -> Nat
public export
Integral Nat where
div = divNat
You can try using divNatNZ instead, which is public exported, though for some reason it expects the non-zero proof explicitly.

Related

I'm really confused about function declarations in Haskell

This is a homework, so I would prefer only tips or a link to where I can learn rather than a full answer. This is what I am given:
allEqual :: Eq a => a -> a -> a -> Bool
What I understand from this is that I am supposed to compare 3 values (in this case a, a, a?) and return whether or not they all equal one another. This is what I tried:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z do
Bool check <- x == y
Bool nextC <- y == z
if check == nextC
then True
else False
I honestly feel completely lost with Haskell, so any tips on how to read functions or declare them would help immensely.
This question already has several other perfectly good answers explaining how to solve your problem. I don’t want to do that; instead, I will go through each line of your code, progressively correct the problems, and hopefully help you understand Haskell a bit better.
First, I’ll copy your code for convenience:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z do
Bool check <- x == y
Bool nextC <- y == z
if check == nextC
then True
else False
The first line is the type signature; this is already explained well in other answers, so I’ll skip this and go on to the next line.
The second line is where you are defining your function. The first thing you’ve missed is that you need an equals sign to define a function: function definition syntax is functionName arg1 arg2 arg3 … = functionBody, and you can’t remove the =. So let’s correct that:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = do
Bool check <- x == y
Bool nextC <- y == z
if check == nextC
then True
else False
The next error is using do notation. do notation is notorious for confusing beginners, so don’t feel bad about misusing it. In Haskell, do notation is only used in specific situations where it is necessary to execute a sequence of statements line by line, and especially when you have some side-effect (like, say, printing to the console) which is executed with each line. Clearly, this doesn’t fit here — all you’re doing is comparing some values and returning a result, which is hardly something which requires line-by-line execution. So let’s get rid of that do:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
let Bool check = x == y
Bool nextC = y == z
in
if check == nextC
then True
else False
(I’ve also replaced the <- binding with let … in …, since <- can only be used within a do block.)
Next, another problem: Bool check is not valid Haskell! You may be familiar with this syntax from other languages, but in Haskell, a type is always specified using ::, and often with a type signature. So I’ll remove Bool before the names and add type signatures instead:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
let check :: Bool
check = x == y
nextC :: Bool
nextC = y == z
in
if check == nextC
then True
else False
Now, at this point, your program is perfectly valid Haskell — you’ll be able to compile it, and it will work. But there’s still a few improvements you can make.
For a start, you don’t need to include types — Haskell has type inference, and in most cases it’s fine to leave types out (although it’s traditional to include them for functions). So let’s get rid of the types in the let:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
let check = x == y
nextC = y == z
in
if check == nextC
then True
else False
Now, check and nextC are only used in one place — giving them names doesn’t do anything, and only serves to make the code less readable. So I’ll inline the definitions of check and nextC into their usages:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
if (x == y) == (y == z)
then True
else False
Finally, I see you have an expression of the form if <condition> then True else False. This is redundant — you can simply return the <condition> with the same meaning. So let’s do that:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = (x == y) == (y == z)
This is much, much better than the code you started with!
(There is actually one more improvement that you can make to this code. At this point, it should be obvious that your code has a bug. Can you find it? And if so, can you fix it? Hint: you can use the && operator to ‘and’ two booleans together.)
Lets start with a function that takes a single Int:
allEqual1Int :: Int -> Bool
allEqual1Int x = True
allEqual1Int' :: Int -> Bool
allEqual1Int' x =
if x == x
then True
else False
If we compare that to your line
allEqual x y z do
we notice that you miss a = and that you don't need a do.
A version for String could look like
allEqual1String' :: String -> Bool
allEqual1String' x =
if x == x
then True
else False
and we observe that the same implementation works for multiple types (Int and String) provided they support ==.
Now a is a type variable, think of it as a variable such that its value is a type. And the requirement that the given type supports == is encoded in the Eq a constraint (Think of it as an interface). Therefore
allEqual :: Eq a => a -> a -> a -> Bool
works for any such type that supports ==.
Haskell is a bit strange language for those who programmed in different languages before. Let's first have a look at this function:
allEqual :: Int -> Int -> Int -> Bool
You can look at this like that: the last "thing" after "->" is a return type. Previews "things" are parameters. From that, we know the function accepts three parameters that are Int and returns the Bool.
Now have a look at your function.
allEqual :: Eq a => a -> a -> a -> Bool
There is an extra syntax "Eq a =>". What it basically does is all the following "a" in declaration must implement Eq. So it is a more generalized version of the first function. It accepts three parameters that implement "Eq" and returns Bool. What the function should probably do is check if all values are equal.
Now let's have a look at your implementation. You are using a do syntax. I feel like it is not the best approach in the beginning. Let's implement a very similar function which checks if all parameters are equal to 3.
allEq3 :: Int -> Int -> Int -> Bool
allEq3 x y z = isEq3 x && isEq3 y && isEq3 z
where
isEq3 :: Int -> Bool
isEq3 x = x == 3
Like in your example, we have three parameters, and we return Bool. In the first line, we call function isEq3 on all the parameters. If all these calls return true allEq3 will also return true. Otherwise, the function will return false. Notice that the function isEq3 is defined below after the keyword "where". This is a very common thing in Haskell.
So what we did here is taking a big problem of checking if all the parameters are equal to 3 and divide it into smaller pieces which check whether a value is equal to 3.
You can improve this implementation a lot but I think this is the best way to take the first steps in Haskell. If you really want to learn this language you should have a look at this.
allEqual :: Eq a => a -> a -> a -> Bool
The signature says: allEqual consumes 3 values of type a; it produces a result of type Bool. The Eq a => part limits the possible operations a can have; it says whatever type a is, it needs to satisfy the requirements defined in Eq. You can find those requirements here: http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Eq
You now know what operations a can do, you can then complete your function by following the type signature.
Here's how you do it:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = x == y && y == z
What does this mean?
The first line defines the function's type signature.
In human words, it would say something like:
There is a function called allEqual for any type a. It requires an instance of Eq a* and takes three parameters, all of type a, and returns a Bool
The second line says:
The function allEqual, for any parameters x, y, and z, should evaluate x == y && y == z, which simply compares that x equals y and y equals z.
* Instances or type classes are a language feature that not many other programming languages have, so if you're confused to what they mean I'd suggest learning about them first.

Looking for explanation about implicit function type vs explicit function type

Considering the following agda module signature:
module EqList {a ℓ} {A : Set a} {_≈_ : Rel A ℓ} (eq≈ : IsEquivalence _≈_) where
We can define the membership in a list, the list inclusion and the list equivalence:
_∈_ : REL A (List A) _
_∈_ x = Any (x ≈_)
_⊑_ : Rel (List A) _
_⊑_ = _⊆_ on flip _∈_
_≋_ : Rel (List A) _
_≋_ = _⊑_ -[ _×_ ]- flip _⊑_
We can then proceed to prove that this last relation is indeed an equivalence relation:
isEq≋ : IsEquivalence _≋_
isEq≋ = record {
refl = id , id ;
sym = swap ;
trans = zip (λ f g → g ∘ f) λ f g → f ∘ g }
The proof of transitivity is what makes my question arise. The previous definition is accepted by Agda while the following is rejected:
trans = zip (flip _∘_) _∘_
The error is as follows:
({x : A} → Any (_≈_ x) i → Any (_≈_ x) j) !=
((x : A) → Any (_≈_ x) j) because one is an implicit function type
and the other is an explicit function type
when checking that the expression _∘_ has type
(x : j ⊑ k) (y : i ⊑ j) → i ⊑ k
While this error feels weird, because both proofs should be equivalent (replacing the expression f by the expression λ x → f x should always yield the same result) I can somewhat understand why it behaves this way : there is intern trouble as to how to instantiate the various implicit arguments of _∘_. But this explanation is only intuitive and not very conclusive, so my question is the following:
Can someone explain in details why the typechecker succeeds in the first case and not in the second ?
As a side note, here is the header of the module, in order for this example to be self contained:
open import Relation.Binary.Core
open import Data.List hiding (zip)
open import Data.List.Any
open import Relation.Unary using (_⊆_)
open import Function
open import Data.Product
As a second side note, I am aware that a similar question has already been answered:
How to get around the implicit vs explicit function type error?
But said answer does not contain any deep explanation about this surprising behavior.

Representing Higher-Order Functors as Containers in Coq

Following this approach, I'm trying to model functional programs using effect handlers in Coq, based on an implementation in Haskell. There are two approaches presented in the paper:
Effect syntax is represented as a functor and combined with the free monad.
data Prog sig a = Return a | Op (sig (Prog sig a))
Due to the termination check not liking non-strictly positive definitions, this data type can't be defined directly. However, containers can be used to represent strictly-positive functors, as described in this paper. This approach works but since I need to model a scoped effect that requires explicit scoping syntax, mismatched begin/end tags are possible. For reasoning about programs, this is not ideal.
The second approach uses higher-order functors, i.e. the following data type.
data Prog sig a = Return a | Op (sig (Prog sig) a)
Now sig has the type (* -> *) -> * -> *. The data type can't be defined in Coq for the same reasons as before. I'm looking for ways to model this data type, so that I can implement scoped effects without explicit scoping tags.
My attempts of defining a container for higher-order functors have not been fruitful and I can't find anything about this topic. I'm thankful for pointers in the right direction and helpful comments.
Edit: One example of scoped syntax from the paper that I would like to represent is the following data type for exceptions.
data HExc e m a = Throw′ e | forall x. Catch′ (m x) (e -> m x) (x -> m a)
Edit2: I have merged the suggested idea with my approach.
Inductive Ext Shape (Pos : Shape -> Type -> Type -> Type) (F : Type -> Type) A :=
ext : forall s, (forall X, Pos s A X -> F X) -> Ext Shape Pos F A.
Class HContainer (H : (Type -> Type) -> Type -> Type):=
{
Shape : Type;
Pos : Shape -> Type -> Type -> Type;
to : forall M A, Ext Shape Pos M A -> H M A;
from : forall M A, H M A -> Ext Shape Pos M A;
to_from : forall M A (fx : H M A), #to M A (#from M A fx) = fx;
from_to : forall M A (e : Ext Shape Pos M A), #from M A (#to M A e) = e
}.
Section Free.
Variable H : (Type -> Type) -> Type -> Type.
Inductive Free (HC__F : HContainer H) A :=
| pure : A -> Free HC__F A
| impure : Ext Shape Pos (Free HC__F) A -> Free HC__F A.
End Free.
The code can be found here. The Lambda Calculus example works and I can prove that the container representation is isomorphic to the data type.
I have tried to to the same for a simplified version of the exception handler data type but it does not fit the container representation.
Defining a smart constructor does not work, either. In Haskell, the constructor works by applying Catch' to a program where an exception may occur and a continuation, which is empty in the beginning.
catch :: (HExc <: sig) => Prog sig a -> Prog sig a
catch p = inject (Catch' p return)
The main issue I see in the Coq implementation is that the shape needs to be parameterized over a functor, which leads to all sorts of problems.
This answer gives more intuition about how to derive containers from functors than my previous one. I'm taking quite a different angle, so I'm making a new answer instead of revising the old one.
Simple recursive types
Let's consider a simple recursive type first to understand non-parametric containers, and for comparison with the parameterized generalization. Lambda calculus, without caring about scopes, is given by the following functor:
Inductive LC_F (t : Type) : Type :=
| App : t -> t -> LC_F t
| Lam : t -> LC_F t
.
There are two pieces of information we can learn from this type:
The shape tells us about the constructors (App, Lam), and potentially also auxiliary data not relevant to the recursive nature of the syntax (none here). There are two constructors, so the shape has two values. Shape := App_S | Lam_S (bool also works, but declaring shapes as standalone inductive types is cheap, and named constructors also double as documentation.)
For every shape (i.e., constructor), the position tells us about recursive occurences of syntax in that constructor. App contains two subterms, hence we can define their two positions as booleans; Lam contains one subterm, hence its position is a unit. One could also make Pos (s : Shape) an indexed inductive type, but that is a pain to program with (just try).
(* Lambda calculus *)
Inductive ShapeLC :=
| App_S (* The shape App _ _ *)
| Lam_S (* The shape Lam _ *)
.
Definition PosLC s :=
match s with
| App_S => bool
| Lam_S => unit
end.
Parameterized recursive types
Now, properly scoped lambda calculus:
Inductive LC_F (f : Type -> Type) (a : Type) : Type :=
| App : f a -> f a -> LC_F a
| Lam : f (unit + a) -> LC_F a
.
In this case, we can still reuse the Shape and Pos data from before. But this functor encodes one more piece of information: how each position changes the type parameter a. I call this parameter the context (Ctx).
Definition CtxLC (s : ShapeLC) : PosLC s -> Type -> Type :=
match s with
| App_S => fun _ a => a (* subterms of App reuse the same context *)
| Lam_S => fun _ a => unit + a (* Lam introduces one variable in the context of its subterm *)
end.
This container (ShapeLC, PosLC, CtxLC) is related to the functor LC_F by an isomorphism: between the sigma { s : ShapeLC & forall p : PosLC s, f (CtxLC s p a) } and LC_F a. In particular, note how the function y : forall p, f (CtxLC s p) tells you exactly how to fill the shape s = App_S or s = Lam_S to construct a value App (y true) (y false) : LC_F a or Lam (y tt) : LC_F a.
My previous representation encoded Ctx in some additional type indices of Pos. The representations are equivalent, but this one here looks tidier.
Exception handler calculus
We'll consider just the Catch constructor. It has four fields: a type X, the main computation (which returns an X), an exception handler (which also recovers an X), and a continuation (consuming the X).
Inductive Exc_F (E : Type) (F : Type -> Type) (A : Type) :=
| ccatch : forall X, F X -> (E -> F X) -> (X -> F A) -> Exc_F E F A.
The shape is a single constructor, but you must include X. Essentially, look at all the fields (possibly unfolding nested inductive types), and keep all the data that doesn't mention F, that's your shape.
Inductive ShapeExc :=
| ccatch_S (X : Type) (* The shape ccatch X _ (fun e => _) (fun x => _) *)
.
(* equivalently, Definition ShapeExc := Type. *)
The position type lists all the ways to get an F out of an Exc_F of the corresponding shape. In particular, a position contains the arguments to apply functions with, and possibly any data to resolve branching of any other sort. In particular, you need to know the exception type to store exceptions for the handler.
Inductive PosExc (E : Type) (s : ShapeExc) : Type :=
| main_pos (* F X *)
| handle_pos (e : E) (* E -> F X *)
| continue_pos (x : getX s) (* X -> F A *)
.
(* The function getX takes the type X contained in a ShapeExc value, by pattern-matching: getX (ccatch_S X) := X. *)
Finally, for each position, you need to decide how the context changes, i.e., whether you're now computing an X or an A:
Definition Ctx (E : Type) (s : ShapeExc) (p : PosExc E s) : Type -> Type :=
match p with
| main_pos | handle_pos _ => fun _ => getX s
| continue_pos _ => fun A => A
end.
Using the conventions from your code, you can then encode the Catch constructor as follows:
Definition Catch' {E X A}
(m : Free (C__Exc E) X)
(h : E -> Free (C__Exc E) X)
(k : X -> Free (C__Exc E) A) : Free (C__Exc E) A :=
impure (#ext (C__Exc E) (Free (C__Exc E)) A (ccatch_S X) (fun p =>
match p with
| main_pos => m
| handle_pos e => h e
| continue_pos x => k x
end)).
(* I had problems with type inference for some reason, hence #ext is explicitly applied *)
Full gist https://gist.github.com/Lysxia/6e7fb880c14207eda5fc6a5c06ef3522
The main trick in the "first-order" free monad encoding is to encode a functor F : Type -> Type as a container, which is essentially a dependent pair { Shape : Type ; Pos : Shape -> Type }, so that, for all a, the type F a is isomorphic to the sigma type { s : Shape & Pos s -> a }.
Taking this idea further, we can encode a higher-order functor F : (Type -> Type) -> (Type -> Type) as a container { Shape : Type & Pos : Shape -> Type -> (Type -> Type) }, so that, for all f and a, F f a is isomorphic to { s : Shape & forall x : Type, Pos s a x -> f x }.
I don't quite understand what the extra Type parameter in Pos is doing there, but It Works™, at least to the point that you can construct some lambda calculus terms in the resulting type.
For example, the lambda calculus syntax functor:
Inductive LC_F (f : Type -> Type) (a : Type) : Type :=
| App : f a -> f a -> LC_F a
| Lam : f (unit + a) -> LC_F a
.
is represented by the container (Shape, Pos) defined as:
(* LC container *)
Shape : Type := bool; (* Two values in bool = two constructors in LC_F *)
Pos (b : bool) : Type -> (Type -> Type) :=
match b with
| true => App_F
| false => Lam_F
end;
where App_F and Lam_F are given by:
Inductive App_F (a : Type) : TyCon :=
| App_ (b : bool) : App_F a a
.
Inductive Lam_F (a : Type) : TyCon :=
| Lam_ : Lam_F a (unit + a)
.
Then the free-like monad Prog (implicitly parameterized by (Shape, Pos)) is given by:
Inductive Prog (a : Type) : Type :=
| Ret : a -> Prog a
| Op (s : Shape) : (forall b, Pos s a b -> Prog b) -> Prog a
.
Having defined some boilerplate, you can write the following example:
(* \f x -> f x x *)
Definition omega {a} : LC a :=
Lam (* f *) (Lam (* x *)
(let f := Ret (inr (inl tt)) in
let x := Ret (inl tt) in
App (App f x) x)).
Full gist: https://gist.github.com/Lysxia/5485709c4594b836113736626070f488

returning two different types from one function

How can I return values of multiple types from a single function?
I want to do something like:
take x y | x == [] = "error : empty list"
| x == y = True
| otherwise = False
I have a background in imperative languages.
There is a type constructor called Either that lets you create a type that could be one of two types. It is often used for handling errors, just like in your example. You would use it like this:
take x y | x == [] = Left "error : empty list"
| x == y = Right True
| otherwise = Right False
The type of take would then be something like Eq a => [a] -> [a] -> Either String Bool. The convention with Either for error handling is that Left represents the error and Right represents the normal return type.
When you have an Either type, you can pattern match against it to see which value it contains:
case take x y of
Left errorMessage -> ... -- handle error here
Right result -> ... -- do what you normally would
There is several solutions to your problem, depending on your intention : do you want to make manifest in your type that your function can fail (and in this case do you want to return the cause of the failure, which may be unnecessary if there is only one mode of failure like here) or do you estimate that getting an empty list in this function shouldn't happen at all, and so want to fail immediately and by throwing an exception ?
So if you want to make explicit the possibility of failure in your type, you can use Maybe, to just indicate failure without explanation (eventually in your documentation) :
take :: (Eq a) => [a] -> [a] -> Maybe Bool
take [] _ = Nothing
take x y = x == y
Or Either to register the reason of the failure (note that Either would be the answer to "returning two types from one function" in general, though your code is more specific) :
take :: (Eq a) => [a] -> [a] -> Either String Bool
take [] _ = Left "Empty list"
take x y = Right $ x == y
Finally you can signal that this failure is completely abnormal and can't be handled locally :
take :: (Eq a) => [a] -> [a] -> Bool
take [] _ = error "Empty list"
take x y = x == y
Note that with this last way, the call site don't have to immediately handle the failure, in fact it can't, since exceptions can only be caught in the IO monad. With the first two ways, the call site have to be modified to handle the case of failure (and can), if only to itself call "error".
There is one final solution that allows the calling code to choose which mode of failure you want (using the failure package http://hackage.haskell.org/package/failure ) :
take :: (Failure String m, Eq a) => [a] -> [a] -> m Bool
take [] _ = failure "Empty list"
take x y = return $ x == y
This can mimics the Maybe and the Either solution, or you can use take as an IO Bool which will throw an exception if it fails. It can even works in a [Bool] context (returns an empty list in case of failure, which is sometimes useful).
You can use the error functions for exceptions:
take :: Eq a => [a] -> [a] -> Bool
take [] _ = error "empty list"
take x y = x == y
The three answers you've gotten so far (from Tikhon Jelvis, Jedai and Philipp) cover the three conventional ways of handling this sort of situation:
Use the error function signal an error. This is often frowned upon, however, because it makes it hard for programs that use your function to recover from the error.
Use Maybe to indicate the case where no Boolean answer can be produced.
Use Either, which is often used to do the same thing as Maybe, but can additionally include more information about the failure (Left "error : empty list").
I'd second the Maybe and Either approach, and add one tidbit (which is slightly more advanced, but you might want to get to eventually): both Maybe and Either a can be made into monads, and this can be used to write code that is neutral between the choice between those two. This blog post discusses eight different ways to tackle your problem, which includes the three mentioned above, a fourth one that uses the Monad type class to abstract the difference between Maybe and Either, and yet four others.
The blog entry is from 2007 so it looks a bit dated, but I managed to get #4 working this way:
{-# LANGUAGE FlexibleInstances #-}
take :: (Monad m, Eq a) => [a] -> [a] -> m Bool
take x y | x == [] = fail "error : empty list"
| x == y = return True
| otherwise = return False
instance Monad (Either String) where
return = Right
(Left err) >>= _ = Left err
(Right x) >>= f = f x
fail err = Left err
Now this take function works with both cases:
*Main> Main.take [1..3] [1..3] :: Maybe Bool
Just True
*Main> Main.take [1] [1..3] :: Maybe Bool
Just False
*Main> Main.take [] [1..3] :: Maybe Bool
Nothing
*Main> Main.take [1..3] [1..3] :: Either String Bool
Right True
*Main> Main.take [1] [1..3] :: Either String Bool
Right False
*Main> Main.take [] [1..3] :: Either String Bool
Left "error : empty list"
Though it's important to note that fail is controversial, so I anticipate reasonable objections to this approach. The use of fail here is not essential, though—it could be replaced with any function f :: (Monad m, ErrorClass m) => String -> m a such that f err is Nothing in Maybe and Left err in Either.

OCAML: Difference between let func a x = (a x);; and let func a x = ((fun a -> a) x);;?

I'm a little stuck on this assignment for OCAML. I'm trying to pass in a function and a value as a parameter into another function. For example, I have function called test that takes in (fun x -> x+x) and 3 as parameters. The function test should output 6, since 3 + 3 = 6. I know I can achieve something similar this by completing:
let func x = x + x;;
let a = func;;
let test a x = (a x);;
This way when I input, test a 5, I will get 10.
But when I change the statement to this, I get only the value I placed in for x. How do I get the (fun a -> a) to take in the value x?
let test a x = ((fun a -> a) x);;
fun a -> a is an anonymous identity function, it will always return its parameter. You could say:
let id = fun a -> a;;
id 3;;
=> 3
id (fun x -> x + x);;
=> (fun x -> x + x)
Note that in your
let test a x = ((fun a -> a) x);;
the first a and the other two as are different variables. The first one is never used again later. You can rewrite this line for easier understanding as:
let test a x = ((fun b -> b) x);;
How do I get the (fun a -> a) to take in the value x?
You are, and that's the problem. You're feeding your x to an identity function, and getting x back. What you want to do is feed the x to your a function, and that's what you did in your first attempt:
let func x = x + x;;
let test a x = a x;;
test func 3;;
=> 6
With functional languages in simple cases it is often helpful to write the expression in a form similar to lambda calculus and do the reductions manually (noting which reductions you are using). You can still use OCaml syntax as a simplified version of lambda calculus
So in the case of your example this would become:
let test a x = ((fun a -> a ) x);;
=> let test a x = ((fun b -> b ) x);; (* Variable renamed (alpha equivalence) *)
=> let test a y = ((fun b -> b ) y);; (* Variable renamed (alpha equivalence) *)
let func x = x + x;;
Note that these steps only serve to make sure, that we will later on have no variables with the same name, referring to different values. These steps can be left out, but I personally like working with unique variables much better.
test func 5
=> test (fun x -> x + x) 5 (* Variable func inserted (beta reduction) *)
=> (fun a y -> ((fun b -> b) y) (fun x -> x + x) 5 (* Variable test inserted *)
=> (fun y -> (fun b -> b) y) 5 (* Variable a inserted *)
=> ((fun b -> b) 5 (* Variable y inserted *)
=> 5 (* Variable b inserted *)
The final result is 5. Attempting this at first will seem very unusual and hard, but get's easier very fast. If you do something like this a couple of time you will get much better at understanding common functional patterns and reasoning about your program structure.
Have a look at this article for more examples on this.
Also note, that with a little more effort, this works backward as well. Although this usually is not as helpful as doing it in the same direction as the compiler.