Propagating errors to cause HUnit tests to fail - unit-testing

I am writing an HUnit test for a function eval :: Wff -> Assignment -> Maybe Bool. Wff is a custom data type which is an abstract parse tree for a simplified subset of boolean expressions:
data Wff = Var Name
| Not Wff
| Or Wff Wff
deriving (Eq)
and Assignment is a type alias for an associative list which gives a boolean value for each variable in a Wff:
type Assignment = [(Name, Bool)]
My current test looks like this:
testEval :: Test
testEval = "Test eval"
~: TestList $ zipWith (\e (Just a) -> e ~=? a) expected (eval wff <$> assignments)
where expected = [True, False]
assignments = [[('p', True)], [('p', False)]]
Right wff = parse wffStr
wffStr = "p"
Both of the tests constructed pass. However, the test is not very robust. If I modify it with a more complicated value for wffStr but make a typo, the pattern Right wff will fail because parse will return a Left String instead of a Rigth Wff. This causes the test run to abort and I'd rather get a failure for the few bad tests and the actual results for the rest. How can I modify my current structure so that the error propagates to cause the test to fail instead of aborting all together?

I don't know anything about HUnit, but: can you just tell it what to do when the parse fails?
testEval = case parse wffStr of
Left _ -> {- use HUnit's functions to make a failing test case -}
Right wff -> "Test eval" ~: TestList $ {- ... -}
where expected = [True, False]
{- ... -}

Related

Subtyping for Yojson element in a yojson list

I meet an error about subtyping.
For this code, List.map (fun ((String goal_feat):> Basic.t) -> goal_feat) (goal_feats_json:> Basic.t list).
I meet the following error in vscode:
This expression cannot be coerced to type
Yojson.Basic.t =
[ Assoc of (string * Yojson.Basic.t) list
| Bool of bool
| Float of float
| Int of int
| List of Yojson.Basic.t list
| Null
| String of string ];
it has type [< String of 'a ] -> 'b but is here used with type
[< Yojson.Basic.t ].
While compiling, I meet the following error.
Error: Syntax error: ')' expected.
If I change the code to List.map (fun ((String goal_feat): Basic.t) -> goal_feat) (goal_feats_json:> Basic.t list), which useq explicit type cast instead of subtyping, then the error disappeared. I can not understand what is the problem with my code when i use subtyping. Much appreciation to anyone who could give me some help.
First of all, most likely the answer that you're looking for is
let to_strings xs =
List.map (function `String x -> x | _ -> assert false) (xs :> t list)
The compiler is telling you that your function is handling only one case and you're passing it a list that may contain many other things, so there is a possibility for runtime error. So it is better to indicate to the compiler that you know that only the variants tagged with String are expected. This is what we did in the example above. Now our function has type [> Yojson.Basic.t].
Now back to your direct question. The syntax for coercion is (expr : typeexpr), however in the fun ((String goal_feat):> Basic.t) -> goal_feat snippet, String goal_feat is a pattern, and you cannot coerce a pattern, so we shall use parenthesized pattern here it to give it the right, more general, type1, e.g.,
let exp xs =
List.map (fun (`String x : t) -> x ) (xs :> t list)
This will tell the compiler that the parameter of your function shall belong to a wider type and immediately turn the error into warning 8,
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
(`Bool _|`Null|`Assoc _|`List _|`Float _|`Int _)
which says what I was saying in the first part of the post. It is usually a bad idea to leave warning 8 unattended, so I would suggest you to use the first solution, or, otherwise, find a way to prove to the compiler that your list doesn't have any other variants, e.g., you can use List.filter_map for that:
let collect_strings : t list -> [`String of string] list = fun xs ->
List.filter_map (function
| `String s -> Some (`String s)
| _ -> None) xs
And a more natural solution would be to return untagged strings (unless you really need the to be tagged, e.g., when you need to pass this list to a function that is polymorphic over [> t] (Besides, I am using t for Yojson.Basic.t to make the post shorter, but you should use the right name in your code). So here is the solution that will extract strings and make everyone happy (it will throw away values with other tags),
let collect_strings : t list -> string list = fun xs ->
List.filter_map (function
| `String s -> Some s
| _ -> None) xs
Note, that there is no need for type annotations here, and we can easily remove them to get the most general polymoprhic type:
let collect_strings xs =
List.filter_map (function
| `String s -> Some s
| _ -> None) xs
It will get the type
[> `String a] list -> 'a list
which means, a list of polymorphic variants with any tags, returning a list of objects that were tagged with the String tag.
1)It is not a limitation that coercion doesn't work on patterns, moreover it wouldn't make any sense to coerce a pattern. The coercion takes an expression with an existing type and upcasts (weakens) it to a supertype. A function parameter is not an expression, so there is nothing here to coerce. You can just annotate it with the type, e.g., fun (x : #t) -> x will say that our function expects values of type [< t] which is less general than the unannotated type 'a. To summarize, coercion is needed when you have a function that accepts an value that have a object or polymorphic variant type, and in you would like at some expressions to use it with a weakened (upcasted type) for example
type a = [`A]
type b = [`B]
type t = [a | b]
let f : t -> unit = fun _ -> ()
let example : a -> unit = fun x -> f (x :> t)
Here we have type t with two subtypes a and b. Our function f is accepting the base type t, but example is specific to a. In order to be able to use f on an object of type a we need an explicit type coercion to weaken (we lose the type information here) its type to t. Notice that, we do not change the type of x per se, so the following example still type checks:
let rec example : a -> unit = fun x -> f (x :> t); example x
I.e., we weakened the type of the argument to f but the variable x is still having the stronger type a, so we can still use it as a value of type a.

OCaml currying example

I am writing an OCaml function that accepts a function type, such as (fun _ -> true) and a list. This is what I currently have:
let drop_until_boolean (x: 'a -> bool) lst =
match lst with
| x -> true
Currently that written statement does not work properly, as it always evaluates to true.
When I call drop_until_boolean (fun _ -> true) [] I want it to return true, and when I call drop_until_boolean (fun _ -> true) ["a"] I want it to return false.
Question Summary: How do I make a function such that drop_until_boolean (fun _ -> true) [] evaluates to true.
Another example: drop_until_boolean (fun s -> s.[0]='z') ["z"] evaluates to true and drop_until_boolean (fun s -> s.[0]='z') ["y"] evaluates to false.
I managed to figure out what I wanted to do, probably did a terrible job explaining it. This is what I wanted.
let drop_until_boolean (x: 'a -> bool) lst = if (x lst) then true else false
Your current function says the following in English:
Take a function, call it x, and a second value of any type. Examine the second value. In all cases, no matter what the value, return true.
The variable x that appears in your match is a new variable that is matched against the second argument. Since it's just a simple variable, it always matches successfully. It has no relationship to the first parameter (which happens to be named x also).
It shouldn't be surprising that this function always returns true.
I'm not at all sure what you want the function to do. The name suggests it will return some trailing portion of the list that you give it. But you seem to be saying that it should return a boolean.
Let's assume that you want to do something reasonably simple with the second argument. You say the second argument is a list. The most common structure for a simple list-processing function is like this:
let rec my_function list =
match list with
| [] ->
(* Handle case of empty list *)
| head :: rest ->
(* Handle case of non-empty list,
probably with recursive call *)
Maybe you could think about this general structure as a possible solution to your problem. I hope it is helpful.

Writing test cases in Haskell for differing types

I've made a library that creates "list like" sequences, and of which many Prelude style functions are implemented. I want to write some test cases for this, to ensure my library is producing correct output, and I thought the easiest way to do this is write some functions, convert the results to lists and compare them to the Prelude results. Lets say we've got this:
import qualified MyLibrary as ML
import qualified Prelude as P
For example I may want the following testcase:
P.take 5 (P.enumFrom 1) == toList (ML.take 5 (ML.enumFrom 1))
Note that ML.enumFrom does not output a list, it outputs it's own datatype.
The above works fine, but notice how I'm "repeating myself" (TM). I have to ensure the left and right side are the same otherwise my test case is erroneous.
Is there a nice way to write test cases like this so I don't have to repeat myself?
The first problem that P.take and ML.take, etc., only look similar – in fact they are totally unrelated functions and the compiler doesn't know anything about their common behaviour. So, as #jd823592 proposed, we need to group them with a typeclass (I used a simple newtype wrapper so the example would be compilable):
import Prelude hiding (take, enumFrom)
import qualified Prelude as P (take, enumFrom)
newtype MySeq a = MySeq [a]
class Sequence s where
take :: Int -> s a -> s a
enumFrom :: Enum a => a -> s a
toList :: s a -> [a]
instance Sequence MySeq where
take n (MySeq xs) = MySeq (P.take n xs)
enumFrom n = MySeq (P.enumFrom n)
toList (MySeq xs) = xs
instance Sequence [] where
take = P.take
enumFrom = P.enumFrom
toList = id
Then we'll try to define some tests using now-unified functions from the class definition. They may just generate a Sequence of any type, and we'll then force them to produce explicit types.
test1 = doTest (take 5 $ enumFrom 1) -- the part in brackets is polymorphic
doTest :: (Eq a, Sequence s) => s a -> Bool
doTest test = ???
Now the second problem is that we pass a polymorphic function as a parameter and then need to instantiate it with different type parameters ([a] and MySeq a in this case). In standard Haskell 2010 it is impossible, but we can exploit the Rank2 (or RankN) extension:
{-# LANGUAGE Rank2Types #-}
<...>
doTest :: forall a . Eq a => (forall s . Sequence s => s a) -> Bool
doTest test = (test `asTypeOf` dummy1) == toList (test `asTypeOf` dummy2) where
dummy1 :: Eq a => [a]
dummy1 = undefined
dummy2 :: Eq a => MySeq a
dummy2 = undefined
This solution is a bit clumsy, but still works. Please feel free to improve.

Avoid the warning "Warning 21: this statement never returns (or has an unsound type.)"

This is the first time that I met the following warning "Warning 21: this statement never returns (or has an unsound type.)" and I don't have an idea how to fix it.
let display_committers_stats response = match response##readyState with
| XmlHttpRequest.DONE ->
begin match response##status with
| 200 ->
Js_client_ui.create_menu_tabs "GitSearchTabs";
let l =
Json_parser.get_commits (Js.to_string response##responseText) in
let values =
Json_parser.group_commits_by_user l
|> List.map (fun (author, commits) ->
Js_data.create_discreteBar_item author (float_of_int commits))
|> Array.of_list
|> Js.array in
let discreteBar_chart =
Js_data.create_discreteBar_chart "Commits-impact" values in
let js_arr = Js.array ([|discreteBar_chart |]) in
Js.Unsafe.fun_call
(Js.Unsafe.variable "create_discreteBar_chart")
[|
Js.Unsafe.inject ((js_arr))
|];
let js_arr =
l
|> List.mapi (fun i commit ->
Js_data.create_timeline_data i commit.Git_data.message
commit.Git_data.time)
|> Array.of_list
|> Js.array in
Js.Unsafe.fun_call
(Js.Unsafe.variable "create_timeline")
[|
Js.Unsafe.inject ((js_arr))
|]
| _ -> Printf.printf "Unexcepted status\n" end
| _ -> Printf.printf "Unexcepted state\n"
The warning show the following line :
Js.Unsafe.fun_call
(Js.Unsafe.variable "create_discreteBar_chart")
[|
Js.Unsafe.inject ((js_arr))
|];
For execute multiples expressions in Ocaml, I know that the issue is to use ; between the expressions but what's is wrong in my function now ? Can I have some tips ?
Try wrapping the call in ignore, i.e. instead of Js.Unsafe.fun_call ...;, ignore (Js.Unsafe.fun_call ...);.
The reason this is happening is because your JS function call has a result type "'b", which is not dependent on any of the argument types. In the OCaml type system, this typically means that the function doesn't return, because the only way to "produce" a value of an arbitrary type 'b from nothing is to raise an exception – that is, to give up trying to produce it.
The sequence expression (semicolon) e1; e2 means complete the first expression, then complete the second one. In this case, OCaml is worried that your e1 (the JS function call) won't complete because of its unconstrained result type, as explained above. That would make the sequence expression pointless, which is why you get the warning. However, we know that the reason e1 has an unconstrained result type isn't because it doesn't complete, but because we are using an unsafe binding to JS.
To get around this, wrap e1 in a call to ignore, which is a function that takes anything and evaluates to unit. Now, ; will "see" unit on its left instead of an unconstrained 'b.
Generally speaking, you always want to have an expression of type unit on the left of a ;. So, even if you have an expression that evaluates to a constrained type (such as a concrete type int or a type parameter that is mentioned in the argument types), if that type is not unit, you should still wrap that expression in ignore.

Testing a function that takes an empty list

I'm trying to write a unit test for the simple function that takes a list and just returns it,
func :: [a] -> [a]
func x = x
using the test code to test that it works as expected when given an empty list
emptyListTest :: Test
emptyListTest = TestCase $ assertEqual "for (func [])," [] $ func []
main :: IO Counts
main = runTestTT $ TestList [emptyListTest]
However, I get the error
No instance for (Show a0) arising from a use of `assertEqual'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Show Double -- Defined in `GHC.Float'
instance Show Float -- Defined in `GHC.Float'
instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
...plus 28 others
In the expression: assertEqual "for (func [])," []
In the second argument of `($)', namely
`assertEqual "for (func [])," [] $ func []'
In the expression:
TestCase $ assertEqual "for (func [])," [] $ func []
Other tests with non-empty lists work fine, and the function works fine when testing manually by calling func [] in ghci.
I've also noticed that if I create a dummy type, and make a list taking elements of that type (if that's the correct way of saying it), then passing that to the test seems to work, and the test passes
data Dummy = Dummy
deriving(Eq, Show)
emptyList :: [Dummy]
emptyList = []
emptyListTest :: Test
emptyListTest = TestCase $ assertEqual "for (func [])," [] $ func emptyList
Why is this? Is there a way to test functions with an empty list without going down the dummy type route?
Well, the error tells you exactly what is wrong. Read it.
The type variable `a0' is ambiguous
So, type your variable! GHC can't possibly know what type to use to test unless you do.
emptyListTest = TestCase $ assertEqual "for (func [])," [] $ func ([] :: [Int])
You may have to enable an extension to do it inline.
You need to provide a type for the empty list - otherwise GHC doesn't know what kind of list you are using.
One possible fix:
.... assertEqual "for (func [])," [] $ func ([] :: [Int])