Want to take your software engineering career to the next level? Join the mailing list for career tips & advice Click here

Statistics on verdict

Number of watchers on Github 26
Number of open issues 10
Average time to close an issue 8 days
Main language Haskell
Average time to merge a PR about 12 hours
Open pull requests 2+
Closed pull requests 0+
Last commit about 4 years ago
Repo Created over 4 years ago
Repo Last Updated over 2 years ago
Size 97 KB
Organization / Authorjkarni
Page Updated
Do you use verdict? Leave a review!
View open issues (10)
View verdict activity
View on github
Fresh, new opensource launches 🚀🚀🚀
Software engineers: It's time to get promoted. Starting NOW! Subscribe to my mailing list and I will equip you with tools, tips and actionable advice to grow in your career.
Evaluating verdict for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)


Very experimental! An introductory talk is available here from BOB 2016 (slides)

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE OverloadedStrings #-}
import GHC.TypeLits
import Data.Proxy
import Data.Monoid
import qualified Data.Text as Text
import Verdict

A very general smart constructor

Sometimes you want to provide extra guarantees about a type that are best expressed in terms of a smart constructor with an opaque type:

newtype NonEmptyList' a = NonEmptyList' { getList :: [a] }
makeNonEmptyList :: [a] -> Maybe (NonEmptyList' a)
makeNonEmptyList x | null x    = Nothing
                   | otherwise = Just (NonEmptyList' x)

Where the NonEmptyList' constructor is not exported.

This works, but it has quite a few downsides. You can no longer derive things like Read and FromJSON without losing the invariants you set up. If you have a NonEmptyList' that is also of maximum length 100, you need to either have a newtype to again wrap your NonEmptyList', or a newtype that expresses both those constraints. Either way, all the functions you wrote for NonEmptyList', and which ideally would work with NonEmptyMax100List, won't do so (without some newtype unwrapping or coercing).

Instead, with verdict you can do this:

type NonEmptyList a = Validated (Not (Length 0)) [a]

The smart constructor comes for free as validate. Specialized for clarity:

mkNonEmpty :: [a] -> Either ErrorTree (NonEmptyList a)
mkNonEmpty = validate

The Read instance also comes for free, and checks for validity:

testRead :: NonEmptyList Int
testRead = read "[]"

testRead will throw a descriptive error message.

With some of the power of refinement types

Writing functions that express their precise assumptions can then be done with Implies:

safeHead :: (c `Implies` (Not (Length 0))) => Validated c [a] -> a
safeHead = head . getVal

Now, if we have another type:

type ReasonableList a = Validated (MinLength 1 :&& MaxLength 100) [a]

Our function safeHead will typecheck when given either a ReasonableList or a NonEmptyList (or anything else with constraints that imply the length is not zero).

The library provides some terms that can be used in the little constraint DSL. Additionally, you may want to create your own. Doing so is as easy as declaring an empty datatype, and a HaskVerdict instance for it. Let us for example define a predicate InCircle cx cy r, which is true of a Point if the point is inside a circle of radius r centered on coordinates (cx, cy):

data InCircle centerX centerY radius
data Point = Point { x :: Integer, y :: Integer } deriving (Eq, Show)

instance (KnownNat cx, KnownNat cy, KnownNat r)
  => HaskVerdict (InCircle cx cy r) Point where
   haskVerdict _
      = check (\p -> (x p - centerX) ^ 2 + (y p - centerY) ^ 2 < radius ^ 2) err
      where centerX = natVal (Proxy :: Proxy cx)
            centerY = natVal (Proxy :: Proxy cy)
            radius = natVal (Proxy :: Proxy r)
            err = "Point not inside circle: " <> (Text.pack $ show ((centerX, centerY), radius))

Additionally, you may also want to declare type instances for Implies' which describe what things imply or are implied by your new constraint.

type instance Implies' (InCircle cx cy r) (InCircle cx cy r') = r <= r'

And opt-in validation capabilities

Sometimes you may not want to or are not able to change the types of records. You can still validate them:

mkUpperRightQuad :: ApplicativeError ErrorTree m
                 => Integer -> Integer -> m Point
mkUpperRightQuad x' y' = Point <$> x' `checkWith` (Proxy :: Proxy (Minimum 0))
                               <*> y' `checkWith` (Proxy :: Proxy (Minimum 0))

If we instantiate ApplicativeError to a short-circuiting applicative (like Either), only the first result will be returned. If we use something like Failure, all of them will:

main :: IO ()
main = do
  print (mkUpperRightQuad 5 3 :: Either ErrorTree Point)
  print (mkUpperRightQuad (-2) 1 :: Either ErrorTree Point)
  print (mkUpperRightQuad (-2) (- 1) :: Either ErrorTree Point)
  print (mkUpperRightQuad (-2) (- 1) :: Failure ErrorTree Point)
  print (mkUpperRightQuad 0 (- 1) :: Either ErrorTree Point)
  print testRead

The results:

Right (Point {x = 5, y = 3})
Left (Leaf "Should be more than 0")
Left (Leaf "Should be more than 0")
Failure (And (Leaf "Should be more than 0") (Leaf "Should be more than 0"))
Left (Leaf "Should be more than 0")
tutorial: Leaf "this still needs to be figured out"
verdict open issues Ask a question     (View All Issues)
  • over 4 years Invalid instance HaskVerdict (a :&& b) r
  • over 4 years Description
  • over 4 years Permit boolean operations over index constraints/predicates
  • over 4 years Indices for constraints of kind * -> *
  • over 4 years Make Read and Show instances match up.
  • over 4 years QuickCheck
  • over 4 years Fix error messages for 'Not'
  • over 4 years Form validation
  • over 4 years Lift constraint
verdict open pull requests (View All Pulls)
  • Groundwork for higher-kinded db indices.
  • Youtube link to BOB 2016 talk
verdict questions on Stackoverflow (View All Questions)
  • C : Runtime Error Verdict (Help debugging)
  • Why my java code of UVA online judge 100 puzzle shows runtime error in verdict
  • How to update test case last verdict in Rally using REST API
  • What's the verdict? Validation in my model, or in the model form?
  • How to read out tc result verdict in an xml file with Python and ElementTree
  • CodeForces Little Pony and Lord Tirek verdict get different output from my PC
  • So what's the verdict about opening an attachment with a custom file extension from the Gmail Android app?
  • Select all judges that had their verdict changed at least half of the times
  • There is no NF_REJECT verdict in libnetfilter_queue
verdict list of languages used
Other projects in Haskell