Skip to content

Commit

Permalink
Remove subsumption from Applicatives (#569)
Browse files Browse the repository at this point in the history
  • Loading branch information
gusty authored Nov 26, 2023
1 parent b1ed24b commit 52fa7b7
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 30 deletions.
67 changes: 37 additions & 30 deletions src/FSharpPlus/Control/Applicative.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,58 @@ open FSharpPlus.Data

type Apply =
inherit Default1

#if (!FABLE_COMPILER || FABLE_COMPILER_3) && !FABLE_COMPILER_4

static member inline ``<*>`` (f: '``Monad<'T->'U>`` , x: '``Monad<'T>`` , [<Optional>]_output: '``Monad<'U>`` , [<Optional>]_mthd:Default2) : '``Monad<'U>`` = Bind.InvokeOnInstance f (fun (x1: 'T->'U) -> Bind.InvokeOnInstance x (fun x2 -> Return.InvokeOnInstance (x1 x2)))
static member inline ``<*>`` (f: '``Applicative<'T->'U>``, x: '``Applicative<'T>``, [<Optional>]_output: '``Applicative<'U>``, [<Optional>]_mthd:Default1) : '``Applicative<'U>`` = ((^``Applicative<'T->'U>`` or ^``Applicative<'T>`` or ^``Applicative<'U>``) : (static member (<*>) : _*_ -> _) f, x)

static member ``<*>`` (f: Lazy<'T->'U> , x: Lazy<'T> , [<Optional>]_output: Lazy<'U> , [<Optional>]_mthd: Apply) = Lazy.apply f x : Lazy<'U>
static member ``<*>`` (f: seq<_> , x: seq<'T> , [<Optional>]_output: seq<'U> , [<Optional>]_mthd: Apply) = Seq.apply f x : seq<'U>
static member ``<*>`` (f: NonEmptySeq<_> , x: NonEmptySeq<'T> , [<Optional>]_output: NonEmptySeq<'U> , [<Optional>]_mthd: Apply) = NonEmptySeq.apply f x : NonEmptySeq<'U>
static member ``<*>`` (f: IEnumerator<_> , x: IEnumerator<'T> , [<Optional>]_output: IEnumerator<'U> , [<Optional>]_mthd: Apply) = Enumerator.map2 id f x : IEnumerator<'U>
static member ``<*>`` (f: list<_> , x: list<'T> , [<Optional>]_output: list<'U> , [<Optional>]_mthd: Apply) = List.apply f x : list<'U>
static member ``<*>`` (f: _ [] , x: 'T [] , [<Optional>]_output: 'U [] , [<Optional>]_mthd: Apply) = Array.apply f x : 'U []
static member ``<*>`` (f: 'r -> _ , g: _ -> 'T , [<Optional>]_output: 'r -> 'U , [<Optional>]_mthd: Apply) = fun x -> let f' = f x in f' (g x) : 'U
static member inline ``<*>`` ((a: 'Monoid, f) , (b: 'Monoid, x: 'T) , [<Optional>]_output: 'Monoid * 'U , [<Optional>]_mthd: Apply) = (Plus.Invoke a b, f x) : 'Monoid *'U
static member inline ``<*>`` (struct (a: 'Monoid, f), struct (b: 'Monoid, x: 'T), [<Optional>]_output: struct ('Monoid * 'U), [<Optional>]_mthd: Apply) = struct (Plus.Invoke a b, f x) : struct ('Monoid * 'U)
static member ``<*>`` (struct (f: Lazy<'T->'U> , x: Lazy<'T> ) , _output: Lazy<'U> , [<Optional>]_mthd: Apply) = Lazy.apply f x : Lazy<'U>
static member ``<*>`` (struct (f: seq<_> , x: seq<'T> ) , _output: seq<'U> , [<Optional>]_mthd: Apply) = Seq.apply f x : seq<'U>
static member ``<*>`` (struct (f: NonEmptySeq<_> , x: NonEmptySeq<'T> ) , _output: NonEmptySeq<'U> , [<Optional>]_mthd: Apply) = NonEmptySeq.apply f x : NonEmptySeq<'U>
static member ``<*>`` (struct (f: IEnumerator<_> , x: IEnumerator<'T> ) , _output: IEnumerator<'U> , [<Optional>]_mthd: Apply) = Enumerator.map2 id f x : IEnumerator<'U>
static member ``<*>`` (struct (f: list<_> , x: list<'T> ) , _output: list<'U> , [<Optional>]_mthd: Apply) = List.apply f x : list<'U>
static member ``<*>`` (struct (f: _ [] , x: 'T [] ) , _output: 'U [] , [<Optional>]_mthd: Apply) = Array.apply f x : 'U []
static member ``<*>`` (struct (f: 'r -> _ , g: _ -> 'T ) , _output: 'r -> 'U , [<Optional>]_mthd: Apply) = fun x -> let f' = f x in f' (g x) : 'U
static member inline ``<*>`` (struct ((a: 'Monoid, f) , (b: 'Monoid, x: 'T) ) , _output: 'Monoid * 'U , [<Optional>]_mthd: Apply) = (Plus.Invoke a b, f x) : 'Monoid *'U
static member inline ``<*>`` (struct (struct (a: 'Monoid, f), struct (b: 'Monoid, x: 'T)), _output: struct ('Monoid * 'U), [<Optional>]_mthd: Apply) = struct (Plus.Invoke a b, f x) : struct ('Monoid * 'U)
#if !FABLE_COMPILER
static member ``<*>`` (f: Task<_> , x: Task<'T> , [<Optional>]_output: Task<'U> , [<Optional>]_mthd: Apply) = Task.apply f x : Task<'U>
static member ``<*>`` (struct (f: Task<_> , x: Task<'T> ), _output: Task<'U> , [<Optional>]_mthd: Apply) = Task.apply f x : Task<'U>
#endif
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
static member ``<*>`` (f: ValueTask<_> , x: ValueTask<'T> , [<Optional>]_output: ValueTask<'U> , [<Optional>]_mthd: Apply) = ValueTask.apply f x : ValueTask<'U>
static member ``<*>`` (struct (f: ValueTask<_> , x: ValueTask<'T> ), _output: ValueTask<'U> , [<Optional>]_mthd: Default2) = ValueTask.apply f x : ValueTask<'U>
#endif
static member ``<*>`` (f: Async<_> , x: Async<'T> , [<Optional>]_output: Async<'U> , [<Optional>]_mthd: Apply) = Async.apply f x : Async<'U>
static member ``<*>`` (f: option<_> , x: option<'T> , [<Optional>]_output: option<'U> , [<Optional>]_mthd: Apply) = Option.apply f x : option<'U>
static member ``<*>`` (f: voption<_> , x: voption<'T> , [<Optional>]_output: voption<'U> , [<Optional>]_mthd: Apply) = ValueOption.apply f x : voption<'U>
static member ``<*>`` (f: Result<_,'E> , x: Result<'T,'E> , [<Optional>]_output: Result<'b,'E> , [<Optional>]_mthd: Apply) = Result.apply f x : Result<'U,'E>
static member ``<*>`` (f: Choice<_,'E> , x: Choice<'T,'E> , [<Optional>]_output: Choice<'b,'E> , [<Optional>]_mthd: Apply) = Choice.apply f x : Choice<'U,'E>
static member inline ``<*>`` (KeyValue(a: 'Key, f), KeyValue(b: 'Key, x: 'T), [<Optional>]_output: KeyValuePair<'Key,'U>, [<Optional>]_mthd: Apply) : KeyValuePair<'Key,'U> = KeyValuePair (Plus.Invoke a b, f x)

static member ``<*>`` (f: Map<'Key,_> , x: Map<'Key,'T> , [<Optional>]_output: Map<'Key,'U> , [<Optional>]_mthd: Apply) : Map<'Key,'U> = Map (seq {
static member ``<*>`` (struct (f: Async<_> , x: Async<'T> ), _output: Async<'U> , [<Optional>]_mthd: Apply) = Async.apply f x : Async<'U>
static member ``<*>`` (struct (f: option<_> , x: option<'T> ), _output: option<'U> , [<Optional>]_mthd: Apply) = Option.apply f x : option<'U>
static member ``<*>`` (struct (f: voption<_> , x: voption<'T> ), _output: voption<'U> , [<Optional>]_mthd: Apply) = ValueOption.apply f x : voption<'U>
static member ``<*>`` (struct (f: Result<_,'E> , x: Result<'T,'E> ), _output: Result<'b,'E> , [<Optional>]_mthd: Apply) = Result.apply f x : Result<'U,'E>
static member ``<*>`` (struct (f: Choice<_,'E> , x: Choice<'T,'E> ), _output: Choice<'b,'E> , [<Optional>]_mthd: Apply) = Choice.apply f x : Choice<'U,'E>
static member inline ``<*>`` (struct (KeyValue(a: 'Key, f), KeyValue(b: 'Key, x: 'T)), _output: KeyValuePair<'Key,'U>, [<Optional>]_mthd: Default2) : KeyValuePair<'Key,'U> = KeyValuePair (Plus.Invoke a b, f x)
static member inline ``<*>`` (struct (f: KeyValuePair2<_,_>, x: KeyValuePair2<_,'T> ), _output: KeyValuePair2<_,'U> , [<Optional>]_mthd: Default2) : KeyValuePair2<'Key,'U> =
let a, b = f.Key, x.Key
let f, x = f.Value, x.Value
KeyValuePair2 (Plus.Invoke a b, f x)

static member ``<*>`` (struct (f: Map<'Key,_> , x: Map<'Key,'T> ) , _output: Map<'Key,'U> , [<Optional>]_mthd: Apply) : Map<'Key,'U> = Map (seq {
for KeyValue(k, vf) in f do
match Map.tryFind k x with
| Some vx -> yield k, vf vx
| _ -> () })

static member ``<*>`` (f: Dictionary<'Key,_>, x: Dictionary<'Key,'T> , [<Optional>]_output: Dictionary<'Key,'U> , [<Optional>]_mthd: Apply) : Dictionary<'Key,'U> =
static member ``<*>`` (struct (f: Dictionary<'Key,_>, x: Dictionary<'Key,'T>) , _output: Dictionary<'Key,'U> , [<Optional>]_mthd: Apply) : Dictionary<'Key,'U> =
let dct = Dictionary ()
for KeyValue(k, vf) in f do
match x.TryGetValue k with
| true, vx -> dct.Add (k, vf vx)
| _ -> ()
dct

static member ``<*>`` (f: IDictionary<'Key,_>, x: IDictionary<'Key,'T> , [<Optional>]_output: IDictionary<'Key,'U> , [<Optional>]_mthd: Apply) : IDictionary<'Key,'U> =
static member ``<*>`` (struct (f: IDictionary<'Key,_>, x: IDictionary<'Key,'T>) , _output: IDictionary<'Key,'U> , [<Optional>]_mthd: Apply) : IDictionary<'Key,'U> =
let dct = Dictionary ()
for KeyValue(k, vf) in f do
match x.TryGetValue k with
| true, vx -> dct.Add (k, vf vx)
| _ -> ()
dct :> IDictionary<'Key,'U>

static member ``<*>`` (f: IReadOnlyDictionary<'Key,_>, x: IReadOnlyDictionary<'Key,'T> , [<Optional>]_output: IReadOnlyDictionary<'Key,'U> , [<Optional>]_mthd: Apply) : IReadOnlyDictionary<'Key,'U> =
static member ``<*>`` (struct (f: IReadOnlyDictionary<'Key,_>, x: IReadOnlyDictionary<'Key,'T>) , _output: IReadOnlyDictionary<'Key,'U> , [<Optional>]_mthd: Apply) : IReadOnlyDictionary<'Key,'U> =
let dct = Dictionary ()
for KeyValue(k, vf) in f do
match x.TryGetValue k with
Expand All @@ -72,23 +73,29 @@ type Apply =
dct :> IReadOnlyDictionary<'Key,'U>

#if !FABLE_COMPILER
static member ``<*>`` (f: Expr<'T->'U>, x: Expr<'T>, [<Optional>]_output: Expr<'U>, [<Optional>]_mthd: Apply) = Expr.Cast<'U> (Expr.Application (f, x))
static member ``<*>`` (struct (f: Expr<'T->'U>, x: Expr<'T>), _output: Expr<'U>, [<Optional>]_mthd: Apply) = Expr.Cast<'U> (Expr.Application (f, x))
#endif
static member ``<*>`` (f: ('T->'U) ResizeArray, x: 'T ResizeArray, [<Optional>]_output: 'U ResizeArray, [<Optional>]_mthd: Apply) = ResizeArray.apply f x : 'U ResizeArray
static member ``<*>`` (struct (f: ('T->'U) ResizeArray, x: 'T ResizeArray), _output: 'U ResizeArray, [<Optional>]_mthd: Apply) = ResizeArray.apply f x : 'U ResizeArray

static member inline Invoke (f: '``Applicative<'T -> 'U>``) (x: '``Applicative<'T>``) : '``Applicative<'U>`` =
let inline call (mthd : ^M, input1: ^I1, input2: ^I2, output: ^R) =
((^M or ^I1 or ^I2 or ^R) : (static member ``<*>`` : _*_*_*_ -> _) input1, input2, output, mthd)
((^M or ^I1 or ^I2 or ^R) : (static member ``<*>`` : struct (_*_) * _ * _ -> _) (struct (input1, input2)), output, mthd)
call(Unchecked.defaultof<Apply>, f, x, Unchecked.defaultof<'``Applicative<'U>``>)


#endif

static member inline InvokeOnInstance (f: '``Applicative<'T->'U>``) (x: '``Applicative<'T>``) : '``Applicative<'U>`` =
((^``Applicative<'T->'U>`` or ^``Applicative<'T>`` or ^``Applicative<'U>``) : (static member (<*>) : _*_ -> _) (f, x))

#if (!FABLE_COMPILER || FABLE_COMPILER_3) && !FABLE_COMPILER_4

type Apply with
static member inline ``<*>`` (struct (f: '``Monad<'T->'U>`` , x: '``Monad<'T>`` ) , _output: '``Monad<'U>`` , [<Optional>]_mthd:Default2) : '``Monad<'U>`` = Bind.InvokeOnInstance f (fun (x1: 'T->'U) -> Bind.InvokeOnInstance x (fun x2 -> Return.InvokeOnInstance (x1 x2)))
static member inline ``<*>`` (struct (_: ^t when ^t : null and ^t: struct, _: ^u when ^u : null and ^u: struct), _output: ^r when ^r : null and ^r: struct, _mthd: Default1) = id

static member inline ``<*>`` (struct (f: '``Applicative<'T->'U>``, x: '``Applicative<'T>``), _output: '``Applicative<'U>``, [<Optional>]_mthd: Default1) : '``Applicative<'U>`` = ((^``Applicative<'T->'U>`` or ^``Applicative<'T>`` or ^``Applicative<'U>``) : (static member (<*>) : _*_ -> _) f, x)


type Lift2 =
inherit Default1

Expand Down
1 change: 1 addition & 0 deletions src/FSharpPlus/Control/Functor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type Map =
static member Map ((x: Result<_,'E> , f: 'T->'U), _mthd: Map) = Result.map f x
static member Map ((x: Choice<_,'E> , f: 'T->'U), _mthd: Map) = Choice.map f x
static member Map ((KeyValue(k, x) , f: 'T->'U), _mthd: Map) = KeyValuePair (k, f x)
static member Map ((x: KeyValuePair2<_, _> , f: 'T->'U), _mthd: Map) = let k, x = x.Key, x.Value in KeyValuePair2 (k, f x)
static member Map ((x: Map<'Key,'T> , f: 'T->'U), _mthd: Map) = Map.map (const' f) x : Map<'Key,'U>
static member Map ((x: Dictionary<_,_> , f: 'T->'U), _mthd: Map) = Dictionary.map f x : Dictionary<'Key,'U>
#if !FABLE_COMPILER
Expand Down
6 changes: 6 additions & 0 deletions src/FSharpPlus/Internals.fs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ type Either<'t,'u> =

type DmStruct = struct end

type KeyValuePair2<'TKey, 'TValue> = struct
val Key : 'TKey
val Value : 'TValue
new (key, value) = { Key = key; Value = value }
end

[<Sealed>]
type Set2<'T when 'T: comparison >() = class end

Expand Down
19 changes: 19 additions & 0 deletions tests/FSharpPlus.Tests/General.fs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ type WrappedSeqE<'s> = WrappedSeqE of 's seq with
static member Reduce (WrappedSeqE x, reduction) = SideEffects.add "Using WrappedSeqE's Reduce"; Seq.reduce reduction x
static member ToSeq (WrappedSeqE x) = SideEffects.add "Using WrappedSeqE's ToSeq"; x

type WrappedSeqF<'s> = WrappedSeqF of 's seq with
interface Collections.Generic.IEnumerable<'s> with member x.GetEnumerator () = (let (WrappedSeqF x) = x in x).GetEnumerator ()
interface Collections.IEnumerable with member x.GetEnumerator () = (let (WrappedSeqF x) = x in x).GetEnumerator () :> Collections.IEnumerator
static member Return x = SideEffects.add "Using WrappedSeqF's Return"; WrappedSeqF (Seq.singleton x)
static member (<*>) (WrappedSeqF f, WrappedSeqF x) = SideEffects.add "Using WrappedSeqF's Apply"; WrappedSeqF (f <*> x)
static member ToList (WrappedSeqF x) = Seq.toList x

type TestNonEmptyCollection<'a> = private { Singleton: 'a } with
interface NonEmptySeq<'a> with
member this.First =
Expand Down Expand Up @@ -1205,6 +1212,18 @@ module Applicative =
Assert.AreEqual ([4;5;6], res456)
Assert.AreEqual (toList (run res9n5), toList (run' res9n5'))

// WrappedSeqC is Monad. Monads are Applicatives => (<*>) should work
let (res3: WrappedSeqC<_>) = WrappedSeqC [(+) 1] <*> WrappedSeqC [2]
CollectionAssert.AreEqual (WrappedSeqC [3], res3)

// Check user defined types implementing IEnumerable don't default to seq<_>
let res4 = WrappedSeqF [(+) 1] <*> WrappedSeqF [3]
Assert.IsInstanceOf<Option<WrappedSeqF<int>>> (Some res4)
CollectionAssert.AreEqual (WrappedSeqF [4], res4)
let res5 = WrappedSeqF [(+)] <*> WrappedSeqF [3] <*> WrappedSeqF [2]
Assert.IsInstanceOf<Option<WrappedSeqF<int>>> (Some res5)
CollectionAssert.AreEqual (WrappedSeqF [5], res5)

let testLift2 () =
let expectedEffects = ["Using WrappedSeqD's Return"; "Using WrappedSeqD's Apply"; "Using WrappedSeqD's Apply"]
SideEffects.reset ()
Expand Down

0 comments on commit 52fa7b7

Please sign in to comment.