I am writing a Xamarin.Forms app using XAML for my views, and I am trying to write an IValueConverter
whose job should be returning false
if the input is "empty" for types where those semantics make sense (strings/lists/sequences/arrays/IEnumerables). I have started with the following, which returns false for empty strings, but I can't figure out how to extend this to lists, sequences, arrays, and IEnumerables:
type FalseIfEmptyConverter() =
interface IValueConverter with
member __.Convert(value:obj, _, _, _) =
match value with
| :? string as s -> (s <> "" && not (isNull s)) |> box
// TODO: extend to enumerables
| x -> invalidOp <| "unsupported type " + x.GetType().FullName
member __.ConvertBack(_, _, _, _) =
raise <| System.NotImplementedException()
Things I've tried that don't work:
:? list<_>
does not match a (boxed) list (at least not of ints) and produces a warning This construct causes code to be less generic than indicated by its type annotations. The type variable implied by the use of a '#', '_' or other type annotation at or near [...] has been constrained to be type 'obj'
:? list<obj>
does not produce the warning, but also doesn't match a boxed list of ints:? seq<_>
and :? seq<obj>
:? System.Collections.Generic.IEnumerable<obj>
and IEnumerable<_>
(and if I place it below a similar seq
match as given above, it warns that the rule will never be matched, which makes sense since AFAIK seq
corresponds to IEnumerable
)Using Foggy Finder's idea to use the non-generic IEnumerable
:
let isEmpty (x:obj) =
match x with
| null -> true
| :? System.Collections.IEnumerable as xs -> xs |> Seq.cast |> Seq.isEmpty
| _ -> invalidOp <| "unsupported type " + x.GetType().FullName
isEmpty "" // true
isEmpty [] // true
isEmpty (set []) // true
isEmpty [||] // true
isEmpty null // true
isEmpty "a" // false
isEmpty [|1|] // false
isEmpty 1 // exception
All of the types that you want to test are sub-types of Seq<'a>
, which is exactly the same as IEnumerable<'a>
(including string
, which is a seq<char>
). But this is also sub-type of a non-generic type called IEnumerable
(note the lack of type parameter). This is similar to an IEnumerable<obj>
, where every item has been boxed. This is why we can cast all of these to IEnumerable
, then use Seq.cast
to convert that into IEnumerable<obj>
so that we can use Seq.empty
, which only works on the generic type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With