mbklein

mbklein

How to check if an argument is a Stream or Stream-like function?

I have a function that can operate on a single struct of a specific type, a list of those structs, or a stream of those structs:

def update_data(%MyStruct{} = my_struct), 
  do: %MyStruct{my_struct | data: "Here's the new data!"}
  
def update_data(%Stream{} = stream),
  do: Stream.map(stream, &update_data/1)

def update_data(structs) when is_list(structs),
  do: Enum.map(structs, &update_data/1)

def update_data({:ok, value}),
  do: {:ok, update_data(value)}

def update_data(value),
  do: IO.inspect(value, label: "update_data fell through")

This is working great under almost all conditions. The only exception is when the last step in the pipe before update_data/1 is Stream.flat_map/2. I have discovered this is because the %Stream{} = stream pattern match is failing, because the value returned from Stream.flat_map/2 isn’t a stream:

Interactive Elixir (1.13.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> [1, 2, 3] |> Stream.map(& &1)
#Stream<[enum: [1, 2, 3], funs: [#Function<47.58486609/1 in Stream.map/2>]]>
iex(2)> [1, 2, 3] |> Stream.each(&IO.puts/1)
#Stream<[enum: [1, 2, 3], funs: [#Function<38.58486609/1 in Stream.each/2>]]>
iex(3)> [1, 2, 3] |> Stream.flat_map(& &1)  
#Function<59.58486609/2 in Stream.transform/3>

I guess that means Stream.transform/3 doesn’t return a new stream, either, but a function as well.

I know I can work around this in a couple of different ways, but none of them seem ideal. I don’t want to make the caller responsible for making sure the thing being piped to update_data/1 is an actual Stream. I also don’t want to assume any given Function that shows up is pipe-able to Stream.map/2.

What’s the best way to get update_data/1 to behave the way I want it to?

Most Liked

LostKobrakai

LostKobrakai

Stream API can take any Enumerable as input. Stream API is simply an API, which operates on Enumerables lazily, while Enum operates eagerly. Therefore there‘s not really a stream datatype in elixir. Any struct can implement the Enumerable protocol and technically become „a stream“ as in a possible input to Stream API.

Given there’s no differenciating factor in the input being Enumerables you’ll need some way to let the caller tell you if they want the Enumerable to be processed lazily or eagerly. That could be different functions or some additional parameters on a single function.

gregvaughn

gregvaughn

The details escape me (I suspect it was a performance optimization/tradeoff), but any arity 2 function will be accepted as a stream. The reasons are deeper in the Enumerable protocol. The function has to comply with the reducer() type (Enumerable — Elixir v1.13.2) to return the proper tuples of the acc() type. You can use a guard of is_function(provided_fun, 2) but it is imperfect.

Where Next?

Popular in Questions Top

New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: &lt;h1&gt;Create Post&lt;/h1&gt; &lt;%= ...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement