-
Notifications
You must be signed in to change notification settings - Fork 217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[#1395] Marshalling Set and HashSet #1405
Changes from 9 commits
b654063
cc091a6
f385dcc
7c9b0e7
74088fa
6c4b41a
b25699e
ea23379
4867d54
e88aafd
d7a6fc7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,6 +71,10 @@ module Dhall | |
, sequence | ||
, list | ||
, vector | ||
, setFromDistinctList | ||
, setIgnoringDuplicates | ||
, hashSetFromDistinctList | ||
, hashSetIgnoringDuplicates | ||
, Dhall.map | ||
, pairFromMapEntry | ||
, unit | ||
|
@@ -119,6 +123,7 @@ import Data.Either.Validation (Validation(..), ealt, eitherToValidation, validat | |
import Data.Fix (Fix(..)) | ||
import Data.Functor.Contravariant (Contravariant(..), (>$<), Op(..)) | ||
import Data.Functor.Contravariant.Divisible (Divisible(..), divided) | ||
import Data.Hashable (Hashable) | ||
import Data.List.NonEmpty (NonEmpty (..)) | ||
import Data.Map (Map) | ||
import Data.Monoid ((<>)) | ||
|
@@ -136,7 +141,7 @@ import Dhall.Import (Imported(..)) | |
import Dhall.Parser (Src(..)) | ||
import Dhall.TypeCheck (DetailedTypeError(..), TypeError, X) | ||
import GHC.Generics | ||
import Lens.Family (LensLike', set, view) | ||
import Lens.Family (LensLike', view) | ||
import Numeric.Natural (Natural) | ||
import Prelude hiding (maybe, sequence) | ||
import System.FilePath (takeDirectory) | ||
|
@@ -149,11 +154,13 @@ import qualified Data.Functor.Compose | |
import qualified Data.Functor.Product | ||
import qualified Data.Map | ||
import qualified Data.Maybe | ||
import qualified Data.List | ||
import qualified Data.List.NonEmpty | ||
import qualified Data.Semigroup | ||
import qualified Data.Scientific | ||
import qualified Data.Sequence | ||
import qualified Data.Set | ||
import qualified Data.HashSet | ||
import qualified Data.Text | ||
import qualified Data.Text.IO | ||
import qualified Data.Text.Lazy | ||
|
@@ -167,10 +174,12 @@ import qualified Dhall.Parser | |
import qualified Dhall.Pretty.Internal | ||
import qualified Dhall.TypeCheck | ||
import qualified Dhall.Util | ||
import qualified Lens.Family | ||
|
||
-- $setup | ||
-- >>> :set -XOverloadedStrings | ||
-- >>> :set -XRecordWildCards | ||
-- >>> import Dhall.Pretty.Internal (prettyExpr) | ||
|
||
type Extractor s a = Validation (ExtractErrors s a) | ||
type MonadicExtractor s a = Either (ExtractErrors s a) | ||
|
@@ -405,8 +414,8 @@ inputWithSettings settings (Type {..}) txt = do | |
let EvaluateSettings {..} = _evaluateSettings | ||
|
||
let transform = | ||
set Dhall.Import.normalizer _normalizer | ||
. set Dhall.Import.startingContext _startingContext | ||
Lens.Family.set Dhall.Import.normalizer _normalizer | ||
. Lens.Family.set Dhall.Import.startingContext _startingContext | ||
|
||
let status = transform (Dhall.Import.emptyStatus _rootDirectory) | ||
|
||
|
@@ -499,8 +508,8 @@ inputExprWithSettings settings txt = do | |
let EvaluateSettings {..} = _evaluateSettings | ||
|
||
let transform = | ||
set Dhall.Import.normalizer _normalizer | ||
. set Dhall.Import.startingContext _startingContext | ||
Lens.Family.set Dhall.Import.normalizer _normalizer | ||
. Lens.Family.set Dhall.Import.startingContext _startingContext | ||
|
||
let status = transform (Dhall.Import.emptyStatus _rootDirectory) | ||
|
||
|
@@ -791,6 +800,126 @@ list = fmap Data.Foldable.toList . sequence | |
vector :: Type a -> Type (Vector a) | ||
vector = fmap Data.Vector.fromList . list | ||
|
||
{-| Decode a `Set` from a `List` | ||
|
||
>>> input (setIgnoringDuplicates natural) "[1, 2, 3]" | ||
fromList [1,2,3] | ||
|
||
Duplicate elements are ignored. | ||
|
||
>>> input (setIgnoringDuplicates natural) "[1, 1, 3]" | ||
fromList [1,3] | ||
|
||
-} | ||
setIgnoringDuplicates :: (Ord a) => Type a -> Type (Data.Set.Set a) | ||
setIgnoringDuplicates = fmap Data.Set.fromList . list | ||
|
||
{-| Decode a `HashSet` from a `List` | ||
|
||
>>> input (hashSetIgnoringDuplicates natural) "[1, 2, 3]" | ||
fromList [1,2,3] | ||
|
||
Duplicate elements are ignored. | ||
|
||
>>> input (hashSetIgnoringDuplicates natural) "[1, 1, 3]" | ||
fromList [1,3] | ||
|
||
-} | ||
hashSetIgnoringDuplicates :: (Hashable a, Ord a) | ||
=> Type a | ||
-> Type (Data.HashSet.HashSet a) | ||
hashSetIgnoringDuplicates = fmap Data.HashSet.fromList . list | ||
|
||
{-| Decode a `Set` from a `List` with distinct elements | ||
|
||
>>> input (setFromDistinctList natural) "[1, 2, 3]" | ||
fromList [1,2,3] | ||
|
||
An error is thrown if the list contains duplicates. | ||
|
||
> >>> input (setFromDistinctList natural) "[1, 1, 3]" | ||
> *** Exception: Error: Failed extraction | ||
> | ||
> The expression type-checked successfully but the transformation to the target | ||
> type failed with the following error: | ||
> | ||
> One duplicate element in the list: 1 | ||
> | ||
|
||
> >>> input (setFromDistinctList natural) "[1, 1, 3, 3]" | ||
> *** Exception: Error: Failed extraction | ||
> | ||
> The expression type-checked successfully but the transformation to the target | ||
> type failed with the following error: | ||
> | ||
> 2 duplicates were found in the list, including 1 | ||
> | ||
|
||
-} | ||
setFromDistinctList :: (Ord a, Show a) => Type a -> Type (Data.Set.Set a) | ||
setFromDistinctList = setHelper Data.Set.size Data.Set.fromList | ||
|
||
{-| Decode a `HashSet` from a `List` with distinct elements | ||
|
||
>>> input (hashSetFromDistinctList natural) "[1, 2, 3]" | ||
fromList [1,2,3] | ||
|
||
An error is thrown if the list contains duplicates. | ||
|
||
> >>> input (hashSetFromDistinctList natural) "[1, 1, 3]" | ||
> *** Exception: Error: Failed extraction | ||
> | ||
> The expression type-checked successfully but the transformation to the target | ||
> type failed with the following error: | ||
> | ||
> One duplicate element in the list: 1 | ||
> | ||
|
||
> >>> input (hashSetFromDistinctList natural) "[1, 1, 3, 3]" | ||
> *** Exception: Error: Failed extraction | ||
> | ||
> The expression type-checked successfully but the transformation to the target | ||
> type failed with the following error: | ||
> | ||
> 2 duplicates were found in the list, including 1 | ||
> | ||
|
||
-} | ||
hashSetFromDistinctList :: (Hashable a, Ord a, Show a) | ||
=> Type a | ||
-> Type (Data.HashSet.HashSet a) | ||
hashSetFromDistinctList = setHelper Data.HashSet.size Data.HashSet.fromList | ||
|
||
|
||
setHelper :: (Eq a, Foldable t, Show a) | ||
=> (t a -> Int) | ||
-> ([a] -> t a) | ||
-> Type a | ||
-> Type (t a) | ||
setHelper size toSet (Type extractIn expectedIn) = Type extractOut expectedOut | ||
where | ||
extractOut (ListLit _ es) = case traverse extractIn es of | ||
Success vSeq | ||
| sameSize -> Success vSet | ||
| otherwise -> extractError err | ||
where | ||
vList = Data.Foldable.toList vSeq | ||
vSet = toSet vList | ||
sameSize = size vSet == Data.Sequence.length vSeq | ||
duplicates = vList Data.List.\\ Data.Foldable.toList vSet | ||
err | length duplicates == 1 = | ||
"One duplicate element in the list: " | ||
<> (Data.Text.pack $ show $ head duplicates) | ||
| otherwise = Data.Text.pack $ unwords | ||
[ show $ length duplicates | ||
, "duplicates were found in the list, including" | ||
, show $ head duplicates | ||
] | ||
Failure f -> Failure f | ||
extractOut expr = typeError expectedOut expr | ||
|
||
expectedOut = App List expectedIn | ||
|
||
{-| Decode a `Map` from a @toMap@ expression or generally a @Prelude.Map.Type@ | ||
|
||
>>> input (Dhall.map strictText bool) "toMap { a = True, b = False }" | ||
|
@@ -935,6 +1064,18 @@ instance Interpret a => Interpret [a] where | |
instance Interpret a => Interpret (Vector a) where | ||
autoWith opts = vector (autoWith opts) | ||
|
||
{-| Note that this instance will throw errors in the presence of duplicates in | ||
the list. To ignore duplicates, use `setIgnoringDuplicates`. | ||
-} | ||
instance (Interpret a, Ord a, Show a) => Interpret (Data.Set.Set a) where | ||
autoWith opts = setFromDistinctList (autoWith opts) | ||
|
||
{-| Note that this instance will throw errors in the presence of duplicates in | ||
the list. To ignore duplicates, use `hashSetIgnoringDuplicates`. | ||
-} | ||
instance (Interpret a, Hashable a, Ord a, Show a) => Interpret (Data.HashSet.HashSet a) where | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
autoWith opts = hashSetFromDistinctList (autoWith opts) | ||
|
||
instance (Ord k, Interpret k, Interpret v) => Interpret (Map k v) where | ||
autoWith opts = Dhall.map (autoWith opts) (autoWith opts) | ||
|
||
|
@@ -1511,7 +1652,7 @@ class Inject a where | |
|
||
{-| Use the default options for injecting a value | ||
|
||
> inject = inject defaultInterpretOptions | ||
> inject = injectWith defaultInterpretOptions | ||
-} | ||
inject :: Inject a => InputType a | ||
inject = injectWith defaultInterpretOptions | ||
|
@@ -1656,8 +1797,26 @@ instance Inject a => Inject [a] where | |
instance Inject a => Inject (Vector a) where | ||
injectWith = fmap (contramap Data.Vector.toList) injectWith | ||
|
||
{-| Note that the ouput list will be sorted | ||
|
||
>>> let x = Data.Set.fromList ["mom", "hi" :: Text] | ||
>>> prettyExpr $ embed inject x | ||
[ "hi", "mom" ] | ||
|
||
-} | ||
|
||
jiegillet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
instance Inject a => Inject (Data.Set.Set a) where | ||
injectWith = fmap (contramap Data.Set.toList) injectWith | ||
injectWith = fmap (contramap Data.Set.toAscList) injectWith | ||
|
||
{-| Note that the ouput list may not be sorted | ||
|
||
>>> let x = Data.HashSet.fromList ["hi", "mom" :: Text] | ||
>>> prettyExpr $ embed inject x | ||
[ "mom", "hi" ] | ||
|
||
-} | ||
instance Inject a => Inject (Data.HashSet.HashSet a) where | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder whether we should sort the elements… 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In math, sets have no notion of ordering so I would tend towards not sorting it... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm having second thoughts, maybe we should sort it... Extra opinion @Gabriel439? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On second thought, this is something that we could change later on. For now, maybe just point out that there's no sorting, and add a doctest that demonstrates it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I've done one for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are correct. Good. |
||
injectWith = fmap (contramap Data.HashSet.toList) injectWith | ||
|
||
instance (Inject a, Inject b) => Inject (a, b) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you document how this instance handles duplicates? Maybe just reference
setFromDistinctList
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There can't be any duplicates here because the source is a
Set
which turns into a DhallList
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I take that as an indication that we should find better names for
Interpret
andInject
. ;)(Take a look at the definition on the line below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh my God, don't I feel silly!
Thank you for taking some of the blame away from me :)