Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Fable.Python.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<Compile Include="stdlib/Os.fs" />
<Compile Include="stdlib/Heapq.fs" />
<Compile Include="stdlib/Itertools.fs" />
<Compile Include="stdlib/Functools.fs" />
<Compile Include="stdlib/Queue.fs" />
<Compile Include="stdlib/String.fs" />
<Compile Include="stdlib/Sys.fs" />
Expand Down
44 changes: 44 additions & 0 deletions src/stdlib/Functools.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/// Type bindings for Python functools module: https://docs.python.org/3/library/functools.html
module Fable.Python.Functools

open Fable.Core

// fsharplint:disable MemberNames

[<Erase>]
type IExports =
// ========================================================================
// Higher-order functions
// ========================================================================

/// Apply a function of two arguments cumulatively to the items of an iterable,
/// reducing it to a single value (fold-left without a seed).
/// See https://docs.python.org/3/library/functools.html#functools.reduce
abstract reduce: func: System.Func<'T, 'T, 'T> * iterable: 'T seq -> 'T

/// Apply a function of two arguments cumulatively to the items of an iterable,
/// starting with the initializer as the seed value (fold-left with a seed).
/// See https://docs.python.org/3/library/functools.html#functools.reduce
[<Emit("$0.reduce($1, $2, $3)")>]
abstract reduce: func: System.Func<'State, 'T, 'State> * iterable: 'T seq * initializer: 'State -> 'State

// ========================================================================
// Caching decorators
// ========================================================================

/// Wrap func with an LRU (least-recently-used) cache of at most maxsize entries.
/// Returns a memoised callable with the same signature as func.
/// Requires Python 3.8+.
/// See https://docs.python.org/3/library/functools.html#functools.lru_cache
[<Emit("$0.lru_cache(maxsize=int($1))($2)")>]
abstract lruCache: maxsize: int * func: ('T -> 'R) -> ('T -> 'R)

/// Wrap func with an unbounded cache (equivalent to lru_cache(maxsize=None)).
/// Requires Python 3.9+.
/// See https://docs.python.org/3/library/functools.html#functools.cache
[<Emit("$0.cache($1)")>]
abstract cache: func: ('T -> 'R) -> ('T -> 'R)

/// Higher-order functions and operations on callable objects
[<ImportAll("functools")>]
let functools: IExports = nativeOnly
1 change: 1 addition & 0 deletions test/Fable.Python.Test.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<Compile Include="TestBuiltinsAttr.fs" />
<Compile Include="TestHeapq.fs" />
<Compile Include="TestItertools.fs" />
<Compile Include="TestFunctools.fs" />
<Compile Include="TestQueue.fs" />
<Compile Include="TestThreading.fs" />
<Compile Include="TestTraceback.fs" />
Expand Down
48 changes: 48 additions & 0 deletions test/TestFunctools.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module Fable.Python.Tests.Functools

open Fable.Python.Testing
open Fable.Python.Functools

[<Fact>]
let ``test reduce sum works`` () =
functools.reduce ((fun a b -> a + b), [ 1; 2; 3; 4; 5 ])
|> equal 15

[<Fact>]
let ``test reduce product works`` () =
functools.reduce ((fun a b -> a * b), [ 1; 2; 3; 4; 5 ])
|> equal 120

[<Fact>]
let ``test reduce with initializer works`` () =
functools.reduce ((fun acc x -> acc + x), [ 1; 2; 3 ], 10)
|> equal 16

[<Fact>]
let ``test reduce string fold with initializer works`` () =
functools.reduce ((fun acc s -> acc + s), [ "b"; "c"; "d" ], "a")
|> equal "abcd"

[<Fact>]
let ``test lruCache memoises results`` () =
let callCount = ResizeArray<int>()
let expensive (x: int) =
callCount.Add x
x * x
let cached = functools.lruCache (128, expensive)
cached 5 |> equal 25
cached 5 |> equal 25
cached 3 |> equal 9
callCount.Count |> equal 2

[<Fact>]
let ``test cache memoises results`` () =
let callCount = ResizeArray<int>()
let expensive (x: int) =
callCount.Add x
x * 2
let cached = functools.cache expensive
cached 7 |> equal 14
cached 7 |> equal 14
cached 4 |> equal 8
callCount.Count |> equal 2
Loading