-
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 5 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,9 @@ module Dhall | |||||||
, sequence | ||||||||
, list | ||||||||
, vector | ||||||||
, set | ||||||||
, setFromDistinctList | ||||||||
, hashSet | ||||||||
, Dhall.map | ||||||||
, pairFromMapEntry | ||||||||
, unit | ||||||||
|
@@ -119,6 +122,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 +140,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 +153,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,6 +173,7 @@ import qualified Dhall.Parser | |||||||
import qualified Dhall.Pretty.Internal | ||||||||
import qualified Dhall.TypeCheck | ||||||||
import qualified Dhall.Util | ||||||||
import qualified Lens.Family | ||||||||
|
||||||||
-- $setup | ||||||||
-- >>> :set -XOverloadedStrings | ||||||||
|
@@ -405,8 +412,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 +506,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 +798,77 @@ 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 (set natural) "[1, 2, 3]" | ||||||||
fromList [1,2,3] | ||||||||
|
||||||||
Duplicate elements are ignored. | ||||||||
-} | ||||||||
set :: (Ord a) => Type a -> Type (Data.Set.Set a) | ||||||||
set = fmap Data.Set.fromList . list | ||||||||
|
||||||||
{-| Decode a `HashSet` from a `List` | ||||||||
|
||||||||
>>> input (hashSet natural) "[1, 2, 3]" | ||||||||
fromList [1,2,3] | ||||||||
|
||||||||
Duplicate elements are ignored. | ||||||||
-} | ||||||||
hashSet :: (Hashable a, Ord a) => Type a -> Type (Data.HashSet.HashSet a) | ||||||||
hashSet = 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 (Type extractIn expectedIn) = Type extractOut expectedOut | ||||||||
where | ||||||||
extractOut (ListLit _ es) = case traverse extractIn es of | ||||||||
Success esSeq | ||||||||
| sameSize -> Success esSet | ||||||||
| length duplicates == 1 -> extractError err1 | ||||||||
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 something like this would make it cheaper to distinguish
Suggested change
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. Why would it be cheaper? 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 the current situation it seems that we need to compute Maybe 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. But f x = case x of
Just x | error "whoops" -> 1
Nothing -> 0
ghci> f Nothing
0 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 not quite sure what you mean. I'm assuming usage like
I'm arguing that even if 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 understand what you mean now, users should be able to pattern match on 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. No worries. I wasn't very clear! |
||||||||
| otherwise -> extractError errN | ||||||||
where | ||||||||
esList = Data.Foldable.toList esSeq | ||||||||
esSet = Data.Set.fromList esList | ||||||||
sameSize = Data.Set.size esSet == Data.Sequence.length esSeq | ||||||||
duplicates = esList Data.List.\\ Data.Foldable.toList esSet | ||||||||
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. If we're serious about the possibility of duplicates due to distinct Dhall terms having the same Haskell representation, this isn't sufficient. 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. At this stage, 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. Ah, yes, my misunderstanding!
|
||||||||
err1 = "One duplicate element in the list: " | ||||||||
<> (Data.Text.pack $ show $ head duplicates) | ||||||||
errN = 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 +1013,12 @@ instance Interpret a => Interpret [a] where | |||||||
instance Interpret a => Interpret (Vector a) where | ||||||||
autoWith opts = vector (autoWith opts) | ||||||||
|
||||||||
instance (Interpret a, Ord a) => Interpret (Data.Set.Set a) where | ||||||||
autoWith opts = set (autoWith opts) | ||||||||
|
||||||||
instance (Interpret a, Hashable a, Ord a) => Interpret (Data.HashSet.HashSet a) where | ||||||||
autoWith opts = hashSet (autoWith opts) | ||||||||
|
||||||||
instance (Ord k, Interpret k, Interpret v) => Interpret (Map k v) where | ||||||||
autoWith opts = Dhall.map (autoWith opts) (autoWith opts) | ||||||||
|
||||||||
|
@@ -1659,6 +1743,9 @@ instance Inject a => Inject (Vector a) where | |||||||
instance Inject a => Inject (Data.Set.Set a) where | ||||||||
injectWith = fmap (contramap Data.Set.toList) injectWith | ||||||||
|
||||||||
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) | ||||||||
|
||||||||
{-| This is the underlying class that powers the `Interpret` class's support | ||||||||
|
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 think this should be the same as
setFromDistinctList
. My reasoning is that by default we should fail loudly if we detect anything wrongThere 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.
Let's maybe keep this definition as ~
setIgnoringDuplicates
orsetAllowingDuplicates
?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.
Yeah, that seems reasonable to me