predicate-typed: Predicates, Refinement types and Dsl

[ bsd3, data, library ] [ Propose Tags ]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0, 0.1.0.1, 0.1.0.2, 0.1.0.3, 0.1.0.4, 0.2.0.0, 0.3.0.0, 0.4.0.0, 0.5.0.0, 0.5.1.0, 0.6.0.0, 0.6.0.1, 0.6.2.0, 0.6.2.1, 0.7.0.0, 0.7.1.0, 0.7.2.0, 0.7.3.0, 0.7.4.0, 0.7.4.1, 0.7.4.2, 0.7.4.3, 0.7.4.4, 0.7.4.5
Change log ChangeLog.md
Dependencies aeson, assoc (>=1), base (>=4.7 && <5), binary, bytestring, comonad, containers, deepseq, directory, ghc-prim, lens, mtl, pcre-heavy (>=1.0.0.2), pcre-light, pretty, pretty-terminal (>=0.1.0.0), QuickCheck, safe, template-haskell, text, th-lift (>=0.8.0.1), these (>=1.0.0), time, tree-view (>=0.5) [details]
License BSD-3-Clause
Copyright Copyright 2019, Grant Weyburne
Author Grant Weyburne <gbwey9@gmail.com>
Maintainer Grant Weyburne <gbwey9@gmail.com>
Category Data
Home page https://github.com/gbwey/predicate-typed#readme
Bug tracker https://github.com/gbwey/predicate-typed/issues
Source repo head: git clone https://github.com/gbwey/predicate-typed
Uploaded by gbwey at 2019-10-26T20:58:12Z
Distributions
Downloads 7497 total (69 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for predicate-typed-0.1.0.3

[back to package description]

predicate-typed

what this library provides:

  1. a dsl for building refinement types
  2. Refined is simple refinement type that just validates the input against a predicate
  3. Refined3 is a more complex refinement type that allows you to change the input
  4. validation against input values
  5. visualisation of each step in the process 6 template haskell methods for creating the refinement types at compile time
  6. ToJSON and FromJSON instances for Refined and Refined3
  7. Read and Show instance for Refined and Refined3
  8. Binary instances for Refined and Refined3
  9. database encoders and decoders using odbc(sqlhandler-odbcalt) or hdbc((sqlhandler-odbc)
  10. quickcheck arbitrary methods
data Refined p a = Refined a
  • a is the input type
  • p predicate on a

If you want to see an evaluation tree with colors for any of the examples, just replace 'ol' with 'o2' or if on unix use 'ou' (unicode)

Examples of Refined (for more information see doctests)

  1. reads in a number and checks to see that it is greater than 99
>prtRefinedIO @(ReadP Int >> Id > 99) ol "123"
Right (Refined {unRefined = "123"})
  1. tries to read in a number but fails
>prtRefinedIO @(ReadP Int >> Id > 99) ol "1x2y3"
Left (FailP "ReadP Int (1x2y3) failed")
  1. reads in a hexadecimal string and checks to see that it is between 99 and 256
>prtRefinedIO @(ReadBase Int 16 >> Between 99 256) ol "000fe"
Right (Refined {unRefined = "000fe"})
  1. reads in a hexadecimal string but fails the predicate check
>prtRefinedIO @(ReadBase Int 16 >> Between 99 253) ol "000fe"
Left FalseP
  1. same as 4. above but now we get details of where it went wrong
>prtRefinedIO @(ReadBase Int 16 >> Between 99 253) o2 "000fe"
  1. reads in a string as time and does simple validation
>prtRefinedIO @(Resplit ":" Id >> Map (ReadP Int) Id >> Len == 3) ol "12:01:05"
Right (Refined {unRefined = "12:01:05"})
  • Resplit ":" Id split using regex using a colon as a delimiter ["12","01","05"]
  • Map (ReadP Int) Id Read in the values as Ints [12,1,5]
  • Len == 3 Check to see that the length of the list of Ints is 3

Testing out predicates

When using Refined the expression in p must result in a True/False
pe2 does not have that restriction so you can run the whole thing or the individual pieces
for less detail use pl
if using a unicode-supported OS then pu gives you nicer rendering than pe2\

>pe2 @(Resplit ":" Id >> Map (ReadP Int) Id >> Len == 3) "12:01:05"

>pe2 @(Resplit ":" Id) "12:01:05"

>pe2 @(Map (ReadP Int) Id) ["12","01","05"]

>pe2 @(Len == 3) [12,1,5]

An example using Refined3 (for more information see doctests and doctests)

>type Hex = '(ReadBase Int 16, Between 0 255, ShowBase 16, String)

>prtEval3PIO (Proxy @Hex) ol "0000fe"
Refined3 {in3 = 254, out3 = "fe"}
  1. ReadBase Int 16 reads a hexadecimal string and returns 254
  2. Between 0 255 checks to make sure the predicate holds ie the number is between 0 and 255
  3. ShowBase 16 formats the output as "fe" which is compatible with the input

run this to get details in color of each evaluation step:

>prtEval3PIO (Proxy @Hex) o2 "0000fe"

***Step 1. Success Initial Conversion(ip) [254] ***

P ReadBase(Int) 16 254 | "0000fe"
|
`- P Id "0000fe"

***Step 2. Success Boolean Check(op) ***

True  True && True
|
+- True  254 >= 0
|  |
|  +- P I
|  |
|  `- P '0
|
`- True  254 <= 255
   |
   +- P I
   |
   `- P '255

***Step 3. Success Output Conversion(fmt) = "fe" ***

P ShowBase 16 fe | 254

Read in the string "0000fe" as input to ReadBase Int 16 and produce 254 as output

>pe2 @(ReadBase Int 16) "0000fe"
PresentT 254

>pe2 @(Between 0 255) 254
TrueT

>pe2 @(ShowBase 16) 254 = "fe"
PresentT "fe"

**_Replace '\[(refinedTH ...)' \](refinedTH' o2 ...)' for an evaluation tree **

Template Haskell versions

ex1 :: Refined (ReadP Int >> Id > 99) String
ex1 = $$(refinedTH "123")

Refined3 is the most useful refined type as you can control the input and output types (see documentation and doctests)

**_Replace '\[(refined3TH ...)' \](refined3TH' o2 ...)' for a colored evaluation tree **

type Hex = '(ReadBase Int 16, Between 0 255, ShowBase 16, String)

$$(refined3TH "0000fe") :: MakeR3 Hex

Here is an example where the predicate fails at compile-time and we choose to show the details using o2.

>type Hex = '(ReadBase Int 16, Between 0 255, ShowBase 16, String)

>$$(refined3TH' o2 "000ffff") :: MakeR3 Hex

<interactive>:18:4: error:
    *
*** Step 1. Success Initial Conversion(ip) [65535] ***

P ReadBase(Int,16) 65535 | "000ffff"
|
`- P Id "000ffff"

*** Step 2. False Boolean Check(op) ***

False True && False
|
+- True  65535 >= 0
|  |
|  +- P I
|  |
|  `- P '0
|
`- False 65535 <= 255
   |
   +- P I
   |
   `- P '255

refined3TH: predicate failed with Step 2. False Boolean Check(op) | FalseP
    * In the Template Haskell splice $$(refined3TH' o0 "000ffff")
      In the expression: $$(refined3TH' o0 "000ffff") :: MakeR3 Hex
      In an equation for `it':
          it = $$(refined3TH' o0 "000ffff") :: MakeR3 Hex

Any valid Read/Show instance can be used with Refined3

>$$(refined3TH "13 % 3") :: ReadShowR Rational
Refined3 {r3In = 13 % 3, r3Out = "13 % 3"}

>$$(refined3TH "2016-11-09") :: ReadShowR Day
Refined3 {r3In = 2016-11-09, r3Out = "2016-11-09"}

An example of an invalid refined3TH call

>$$(refined3TH "2016-xy-09") :: ReadShowR Day

<interactive>:171:4: error:
    * refined3TH: predicate failed with Step 1. Initial Conversion(ip) Failed | ReadP Day (2016-xy-09) failed
    * In the Template Haskell splice $$(refined3TH "2016-xy-09")
      In the expression: $$(refined3TH "2016-xy-09") :: ReadShowR Day
      In an equation for `it':
          it = $$(refined3TH "2016-xy-09") :: ReadShowR Day

Json decoding

This example is successful as it is a valid hexadecimal and is in the range 10 though 256

>eitherDecode' @(Refined3 (ReadBase Int 16) (Id > 10 && Id < 256) (ShowP Id) String) "\"00fe\""
Right (Refined3 {in3 = 254, out3 = "254"})

This example fails as the value is not a valid hexadecimal string

>either putStrLn print $ eitherDecode' @(Refined3 (ReadBase Int 16) 'True (ShowP Id) String) "\"00feg\""
Error in $: Refined3:Step 1. Initial Conversion(ip) Failed | invalid base 16

***Step 1. Initial Conversion(ip) Failed ***

[Error invalid base 16] ReadBase(Int) 16 as=00feg err=[(254,"g")]
|
`- P Id "00feg"

This example fails as the hexadecimal value is valid but is not between 10 and 256

>either putStrLn print $ eitherDecode' @(Refined3 (ReadBase Int 16) (Id > 10 && Id < 256) (ShowP Id) String) "\"00fe443a\""
Error in $: Refined3:Step 2. False Boolean Check(op) | FalseP

***Step 1. Success Initial Conversion(ip) [16663610] ***

P ReadBase(Int,16) 16663610 | "00fe443a"
|
`- P Id "00fe443a"

***Step 2. False Boolean Check(op) = FalseP ***

False True && False
|
+- True  16663610 > 10
|  |
|  +- P Id 16663610
|  |
|  `- P '10
|
`- False 16663610 < 256
   |
   +- P Id 16663610
   |
   `- P '256