diff --git a/src/org/rascalmpl/library/Set.rsc b/src/org/rascalmpl/library/Set.rsc index 39c8066445a..fa5d90897b3 100644 --- a/src/org/rascalmpl/library/Set.rsc +++ b/src/org/rascalmpl/library/Set.rsc @@ -276,7 +276,13 @@ public default (&T <:num) sum({(&T <: num) e, *(&T <: num) r}) @synopsis{Pick an arbitrary element from a set.} @description{ +This _randomly_ picks one element from a set, unless the set is empty. +:::warning +Use ((getSingleFrom)) if you want the element from a singleton set. ((getOneFrom)) will silently +continue even if there are more element present, which can be a serious threat to the validity of your +analysis algorithm (arbitrary data is not considered). +::: } @examples{ ```rascal-shell @@ -287,6 +293,16 @@ getOneFrom({"elephant", "zebra", "snake"}); getOneFrom({"elephant", "zebra", "snake"}); ``` } +@benefits{ +* Random sampling can be an effective test input selection strategy. +} +@pitfalls{ +* The name ((getOneFrom)) does not convey randomness. +* ((getOneFrom)) drops all the other elements. +If you are sure there is only one element and you need it, then use ((getSingleFrom)). It will fail fast if your assumption is wrong. +* If you need more then one element, then repeatedly calling ((getOneFrom)) will be expensive. Have a look at ((util::Sampling)) for more effective +sampling utilities. +} @javaClass{org.rascalmpl.library.Prelude} public java &T getOneFrom(set[&T] st); @@ -296,13 +312,42 @@ public java &T getOneFrom(set[&T] st); Get "first" element of a set. Of course, sets are unordered and do not have a first element. However, we may assume that sets are internally ordered in some way and this ordering is reproducible. Applying `getFirstFrom` on the same set will always returns the same element. + +:::warning +Use ((getSingleFrom)) if you want the element from a singleton set. ((getFirstFrom)) will silently +continue even if there are more element present, which can be a serious threat to the validity of your +analysis algorithm (arbitrary data is not considered). +::: } @benefits{ This function helps to make set-based code more deterministic, for instance, for testing purposes. } +@pitfalls{ +* There are much better ways to iterate over the elements of a set: + * Use the `<-` enumerator operator in a `for` loop or a comprehension. + * Use list matching +* ((getFirstFrom)) drops all the other elements + * If you are sure there is only one element and you need it, then use ((getSingleFrom)). It will fail fast if your assumption is wrong. +} @javaClass{org.rascalmpl.library.Prelude} public java &T getFirstFrom(set[&T] st); +@synopsis{Get the only element from a singleton set.} +@description{ +Get the only element of a singleton set. This fails with a ((CallFailed)) exception when the set is not a singleton. +} +@benefits{ +* getSingleFrom fails _fast_ if the assumption (parameter `st` is a singleton) is not met. +* If a binary relation `r` is injective (i.e. it models a function where each key only has one value) then all projections `r[key]` should produce singleton values: `{v}`. +Using ((getSingleFrom)) to get the element out makes sure we fail fast in case our assumptions were wrong, or they have changed. +* Never use ((getFirstFrom)) or ((takeOneFrom)) if you can use ((getSingleFrom)). +} +@pitfalls{ +* ((CallFailed)) exceptions are sometimes hard to diagnose. Look at the stack trace to see that it was ((getSingleFrom)) +that caused it, and then look at the paramete of ((CallFailed)) to see that the set was not a singleton. +} +public &T getSingleFrom(set[&T] st) = getFirstFrom(st) when size(st) == 1; + // TODO temporary? replacement due to unexplained behaviour of compiler //public &T getFirstFrom({&T f, *&T _}) = f; //public &T getFirstFrom(set[&T] _:{}) { throw EmptySet(); } diff --git a/src/org/rascalmpl/library/lang/rascal/tests/basic/Sets.rsc b/src/org/rascalmpl/library/lang/rascal/tests/basic/Sets.rsc index 380af2b0340..ce930223f50 100644 --- a/src/org/rascalmpl/library/lang/rascal/tests/basic/Sets.rsc +++ b/src/org/rascalmpl/library/lang/rascal/tests/basic/Sets.rsc @@ -121,6 +121,13 @@ test bool tst_takeOneFrom(set[int] S) { return x in S && x notin S2 && size(S2) == size(S) - 1 && S2 < S; } +test bool tst_getSingleFrom(set[int] S) { + if ({e} := S) { + return getSingleFrom(s) == e; + } + return false; +} + test bool tst_toList(set[int] S) = isEmpty(S) || size(S) == size(toList(S)) && all(x <- S, x in toList(S)); test bool tst_toMap(rel[int, int] S) = isEmpty(S) || domain(S) == domain(toMap(S)) && range(S) == {*toMap(S)[k] | k <- toMap(S)};