Is there an equivalent of Haskell's fromMaybe?

Is there a Julia equivalent of (or well worn idiom for) Haskell’s fromMaybe, e.g., for use with Union{T, Nothing}?

something seems to be it:

help?> something
search: something @something

  something(x...)

  Return the first value in the arguments which is not equal to nothing, if any. Otherwise throw an error. Arguments of type Some are
  unwrapped.
1 Like

Is it the case though that something(x, y, ...) evaluates y even if x is not nothing?

something is a function like any other; Julia is not lazy like Haskell. Arguments are evaluated first.

(types of arguments are also not part of the type of a function)

Yeah sorry that was a dumb question. I hadn’t realized that. (But ugh!)

There’s the macro @something for that.

4 Likes

Note that @something is an AST transform (commonly known as a macro), not a function:

julia> @macroexpand @something a b    
:(something(let val                   
          #= some.jl:143 =#           
          if (val = a) !== nothing    
              val                     
          else                        
              if (val = b) !== nothing
                  val                 
              else                    
                  nothing             
              end                     
          end                         
      end))                           

This works because all expressions return their last value (in this case a, b or nothing as a fallback).

2 Likes

Is Maybe.jl widely used? I’m new and don’t want to gum things up trying to make Julia something (see what I did there) that it isn’t. Better to deal with its idioms.

According to JuliaHub, Maybe.jl has 0 Dependents, so I’d say no.

Usually, nothing and missing values are handled immediately instead of deferred to a later point in time. Maybe you can tell us more about what you want to code up and we can suggest some way of achieving it?

1 Like

@something was merged into Base. So it’s definitely idiomatic.

I can’t figure out how to invoke @something. I’ve tried @something(...) and Base.@something(...).

Just like in the @macroexpand call I’ve used above:

julia> a = 1                                    
1                                               
                                                
julia> b = nothing                              
                                                
julia> @something a b                           
1                                               
                                                
julia> @something b a                           
1                                               
                                                
julia> @something b b                           
ERROR: ArgumentError: No value arguments present
Stacktrace:                                     
 [1] something()                                
   @ Base ./some.jl:99                          
 [2] something(::Nothing)                       
   @ Base ./some.jl:100                         
 [3] top-level scope                            
   @ some.jl:143                                

Ah my bad! @something was a very recent addition, and is not in the LTS version (or 1.6 I think).

You are right that something(...) evaluates it’s arguments.

To emulate @something, i.e. have fromMaybe which isn’t eager, you can use the if...else pattern shown above.

Yes, the @something macro will be available from 1.7 onwards.

Use the something function in combination with the Some type.

Right now, just test driving the language. To actually do what I want to do it’s probably simpler (and clearer) to just do something like:

    if haskey(things, n)
        return things[n]
    else
       return a_new_thing
    end

rather than

    @something( get(things, n, nothing), a_new_thing )

In practice making a_new_thing has some setup so the former makes sense anyway unless I use let.

For reference the equivalent Haskell that inspired the question is something like

get_thing n = fromMaybe (Thing n part_1 part_2) (M.lookup n things)
    where
        part_1 = a_bunch_of_stuff_that_doest_fit_above
        part_2 = more_stuff_that_doest_fit_above

Sounds like you want to skip the nothing and use get! or get directly:

 get!(f::Function, collection, key)                                                                                   
                                                                                                                      
 Return the value stored for the given key, or if no mapping for the key is present, store key => f(), and return f().

Usage:

julia> d = Dict()                                         
Dict{Any, Any}()                                          
                                                          
julia> get!(() -> begin println("hello"); 1 end, d, "key")
hello                                                     
1                                                         
                                                          
julia> get!(() -> begin println("hello"); 1 end, d, "key")
1                                                         
                                                          
julia> d                                                  
Dict{Any, Any} with 1 entry:                              
  "key" => 1                                              

By the way, this kind of docstring can be easily accessed by using the REPL help mode, invoked by entering ? at the REPL prompt.

2 Likes

That’s good, except I don’t want to store the generated one.

You can do that with get (no exclamation mark):

julia> d = Dict()                                        
Dict{Any, Any}()                                         
                                                         
julia> get(() -> begin println("hello"); 1 end, d, "key")
hello                                                    
1                                                        
                                                         
julia> get(() -> begin println("hello"); 1 end, d, "key")
hello                                                    
1                                                        
                                                         
julia> d                                                 
Dict{Any, Any}()                                         
                                                         
julia> d["key"] = 44                                     
44                                                       
                                                         
julia> get(() -> begin println("hello"); 1 end, d, "key")
44                                                       

It’s a convention (not enforced by the compiler) that functions that modify one or more of their arguments end in a !. There’s no auto generation of such modifying functions, but most of the time when one version exists, the other does as well.

2 Likes