# Inconsistency between map and fmap

**URL:** <https://discourse.julialang.org/t/inconsistency-between-map-and-fmap/106217>\
**Category:** General Usage\
**Tags:** question, functors\
**Created:** [November 14, 2023, 2:16pm UTC](https://discourse.julialang.org/t/inconsistency-between-map-and-fmap/106217 "2023-11-14T14:16:36Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)\
**Post date:** [November 14, 2023, 2:16pm UTC](https://discourse.julialang.org/t/inconsistency-between-map-and-fmap/106217/1 "2023-11-14T14:16:36Z")

</div>

Hi,  
I saw `fmap` as an extension of `map` to functors and I want to use it convert type of element of arrays that are leaves of some struct:  
Taking the doc example:

```plaintext
julia> using Functors

julia> struct Foo
       x
       y
     end

julia> @functor Foo

julia> model = Foo(1, [1, 2, 3])
Foo(1, [1, 2, 3])

julia> fmap(float, model)
Foo(1.0, [1.0, 2.0, 3.0])

julia> fmap(Float32,model)
ERROR: MethodError: no method matching Float32(::Vector{Int64})

Closest candidates are:
(::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number}
 @ Base char.jl:50
(::Type{T})(::Base.TwicePrecision) where T<:Number
 @ Base twiceprecision.jl:266
(::Type{T})(::Complex) where T<:Real
 @ Base complex.jl:44
...

Stacktrace:
[1] (::Functors.ExcludeWalk{Functors.DefaultWalk, DataType, typeof(Functors.isleaf)})(::Function, ::Vector{Int64})
  @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:106
[2] (::Functors.CachedWalk{Functors.ExcludeWalk{Functors.DefaultWalk, DataType, typeof(Functors.isleaf)}, Functors.NoKeyword})(::Function, ::Vector{Int64})
  @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:146
[3] (::Functors.var"#recurse#19"{Functors.CachedWalk{Functors.ExcludeWalk{Functors.DefaultWalk, DataType, typeof(Functors.isleaf)}, Functors.NoKeyword}})(xs::Vector{Int64})
  @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:37
[4] map
  @ ./tuple.jl:274 [inlined]
[5] map(::Function, ::NamedTuple{(:x, :y), Tuple{Int64, Vector{Int64}}})
  @ Base ./namedtuple.jl:219
[6] _map(f::Function, x::NamedTuple{(:x, :y), Tuple{Int64, Vector{Int64}}})
  @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:1
[7] (::Functors.DefaultWalk)(::Function, ::Foo)
  @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:76
[8] ExcludeWalk
  @ ~/.julia/packages/Functors/rlD70/src/walks.jl:106 [inlined]
[9] CachedWalk
  @ ~/.julia/packages/Functors/rlD70/src/walks.jl:146 [inlined]
[10] execute(::Functors.CachedWalk{Functors.ExcludeWalk{Functors.DefaultWalk, DataType, typeof(Functors.isleaf)}, Functors.NoKeyword}, ::Foo)
  @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:38
[11] fmap(::Type, ::Foo; exclude::Function, walk::Functors.DefaultWalk, cache::IdDict{Any, Any}, prune::Functors.NoKeyword)
  @ Functors ~/.julia/packages/Functors/rlD70/src/maps.jl:11
[12] fmap(::Type, ::Foo)
  @ Functors ~/.julia/packages/Functors/rlD70/src/maps.jl:3
[13] top-level scope
  @ REPL[59]:1

```

However if we use map we have

```plaintext
julia> r = [1,2,3];

julia> map(Float32, r)
3-element Vector{Float32}:
 1.0
 2.0
 3.0

julia> fmap(Float32, r)
ERROR: MethodError: no method matching Float32(::Vector{Int64})

Closest candidates are:
  (::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number}
   @ Base char.jl:50
  (::Type{T})(::Base.TwicePrecision) where T<:Number
   @ Base twiceprecision.jl:266
  (::Type{T})(::Complex) where T<:Real
   @ Base complex.jl:44
  ...

Stacktrace:
 [1] (::Functors.ExcludeWalk{Functors.DefaultWalk, DataType, typeof(Functors.isleaf)})(::Function, ::Vector{Int64})
   @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:106
 [2] (::Functors.CachedWalk{Functors.ExcludeWalk{Functors.DefaultWalk, DataType, typeof(Functors.isleaf)}, Functors.NoKeyword})(::Function, ::Vector{Int64})
   @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:146
 [3] execute(::Functors.CachedWalk{Functors.ExcludeWalk{Functors.DefaultWalk, DataType, typeof(Functors.isleaf)}, Functors.NoKeyword}, ::Vector{Int64})
   @ Functors ~/.julia/packages/Functors/rlD70/src/walks.jl:38
 [4] fmap(::Type, ::Vector{Int64}; exclude::Function, walk::Functors.DefaultWalk, cache::IdDict{Any, Any}, prune::Functors.NoKeyword)
   @ Functors ~/.julia/packages/Functors/rlD70/src/maps.jl:11
 [5] fmap(::Type, ::Vector{Int64})
   @ Functors ~/.julia/packages/Functors/rlD70/src/maps.jl:3
 [6] top-level scope
   @ REPL[52]:1

```

Is there any reason why fmap has a different behaviour than map when applied to Arrays?  
How can I do that otherwise?  
Thanks,

Ferréol

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 14, 2023, 2:44pm UTC](https://discourse.julialang.org/t/inconsistency-between-map-and-fmap/106217/2 "2023-11-14T14:44:19Z")

</div>

> [@FerreolS](#):
>
> I saw `fmap` as an extension of `map` to functors and I want to use it convert type of element of arrays that are leaves of some struct:

It definitely seems that way from Functors.jl [home page](https://fluxml.ai/Functors.jl/stable/)’s link to the wikipedia article on [functors in a functional programming context](https://en.wikipedia.org/wiki/Functor_(functional_programming)).

> [@FerreolS](#):
>
> `ERROR: MethodError: no method matching Float32(::Vector{Int64})`

But it appears that `fmap` hits a limit here because `Array` isn’t a struct with finite fields to operate over, so `float([1, 2, 3])` is being called, not `float(1)` etc. `Float32([1, 2, 3])` fails because there’s no method defined for this call.

Just in case, also know that “functors” is a common name for [callable instances](https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects) in base Julia, it’s a different concept entirely.

---

<div class="post-metadata">

**Author:** ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)\
**Post date:** [November 14, 2023, 3:48pm UTC](https://discourse.julialang.org/t/inconsistency-between-map-and-fmap/106217/3 "2023-11-14T15:48:38Z")

</div>

Thank you for your rapid answer.

> But it appears that `fmap` hits a limit here because `Array` isn’t a struct with finite fields to operate over, so `float([1, 2, 3])` is being called, not `float(1)` etc. `Float32([1, 2, 3])` fails because there’s no method defined for this call.

IMO it is more a bug than a feature, should I propose a fallback such as:

```julia
function fmap(F, x)
    try
        fmap(F, x)
    catch
        map(F,x)
    end
end

```

or I can mimick the scheme used in [functors.jl](https://github.com/FluxML/Flux.jl/blob/master/src/functor.jl) from Flux.jl but it is not very convenient
