{"action":"created","issue":{"url":"https://api.github.com/repos/javaslang/javaslang/issues/185","labels_url":"https://api.github.com/repos/javaslang/javaslang/issues/185/labels{/name}","comments_url":"https://api.github.com/repos/javaslang/javaslang/issues/185/comments","events_url":"https://api.github.com/repos/javaslang/javaslang/issues/185/events","html_url":"https://github.com/javaslang/javaslang/issues/185","id":70514555,"number":185,"title":"Deeply map a nested, monadic structure.","user":{"login":"danieldietrich","id":743833,"avatar_url":"https://avatars.githubusercontent.com/u/743833?v=3","gravatar_id":"","url":"https://api.github.com/users/danieldietrich","html_url":"https://github.com/danieldietrich","followers_url":"https://api.github.com/users/danieldietrich/followers","following_url":"https://api.github.com/users/danieldietrich/following{/other_user}","gists_url":"https://api.github.com/users/danieldietrich/gists{/gist_id}","starred_url":"https://api.github.com/users/danieldietrich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/danieldietrich/subscriptions","organizations_url":"https://api.github.com/users/danieldietrich/orgs","repos_url":"https://api.github.com/users/danieldietrich/repos","events_url":"https://api.github.com/users/danieldietrich/events{/privacy}","received_events_url":"https://api.github.com/users/danieldietrich/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/javaslang/javaslang/labels/enhancement","name":"enhancement","color":"84b6eb"},{"url":"https://api.github.com/repos/javaslang/javaslang/labels/[collection]","name":"[collection]","color":"d4c5f9"},{"url":"https://api.github.com/repos/javaslang/javaslang/labels/[control]","name":"[control]","color":"d4c5f9"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/javaslang/javaslang/milestones/15","html_url":"https://github.com/javaslang/javaslang/milestones/1.2.1%20Stream%20Enhancements","labels_url":"https://api.github.com/repos/javaslang/javaslang/milestones/15/labels","id":929759,"number":15,"title":"1.2.1 Stream Enhancements","description":"Implement non-blocking streams as lazy linked lists based on Iteratee/Enumerator.","creator":{"login":"danieldietrich","id":743833,"avatar_url":"https://avatars.githubusercontent.com/u/743833?v=3","gravatar_id":"","url":"https://api.github.com/users/danieldietrich","html_url":"https://github.com/danieldietrich","followers_url":"https://api.github.com/users/danieldietrich/followers","following_url":"https://api.github.com/users/danieldietrich/following{/other_user}","gists_url":"https://api.github.com/users/danieldietrich/gists{/gist_id}","starred_url":"https://api.github.com/users/danieldietrich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/danieldietrich/subscriptions","organizations_url":"https://api.github.com/users/danieldietrich/orgs","repos_url":"https://api.github.com/users/danieldietrich/repos","events_url":"https://api.github.com/users/danieldietrich/events{/privacy}","received_events_url":"https://api.github.com/users/danieldietrich/received_events","type":"User","site_admin":false},"open_issues":2,"closed_issues":35,"state":"open","created_at":"2015-01-10T13:27:45Z","updated_at":"2015-04-23T21:13:42Z","due_on":null,"closed_at":null},"comments":3,"created_at":"2015-04-23T21:13:42Z","updated_at":"2015-04-24T22:25:07Z","closed_at":null,"body":"@ggalmazor proposed:\r\n\r\n```java\r\nMonad<Monad<U>> result = ((Monad<Monad<T>>) unit).flatMap((λ<T,U>) mapper)\r\n```\r\n\r\n...for any number of depth levels.\r\n... and any Monad type (Try, Option, Stream...)"},"comment":{"url":"https://api.github.com/repos/javaslang/javaslang/issues/comments/96082431","html_url":"https://github.com/javaslang/javaslang/issues/185#issuecomment-96082431","issue_url":"https://api.github.com/repos/javaslang/javaslang/issues/185","id":96082431,"user":{"login":"danieldietrich","id":743833,"avatar_url":"https://avatars.githubusercontent.com/u/743833?v=3","gravatar_id":"","url":"https://api.github.com/users/danieldietrich","html_url":"https://github.com/danieldietrich","followers_url":"https://api.github.com/users/danieldietrich/followers","following_url":"https://api.github.com/users/danieldietrich/following{/other_user}","gists_url":"https://api.github.com/users/danieldietrich/gists{/gist_id}","starred_url":"https://api.github.com/users/danieldietrich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/danieldietrich/subscriptions","organizations_url":"https://api.github.com/users/danieldietrich/orgs","repos_url":"https://api.github.com/users/danieldietrich/repos","events_url":"https://api.github.com/users/danieldietrich/events{/privacy}","received_events_url":"https://api.github.com/users/danieldietrich/received_events","type":"User","site_admin":false},"created_at":"2015-04-24T22:25:07Z","updated_at":"2015-04-24T22:25:07Z","body":"I have a local running version. Basically it is a recursive `map` with `Match` cases.\r\n\r\n`Monad` gets a new method `treeMap` which traverses a tree of Monads and transforms values.\r\n\r\n```java\r\ninterface Monad<T> {\r\n    default <U, Z> Monad<Z, M> treeMap(Function<U, Object> mapper) {\r\n        return (Monad<Z, M>) map(Match\r\n                .<Object> caze((Monad m) -> m.treeMap(mapper))\r\n                .caze((U u) -> f.apply(u))\r\n                .build()\r\n        );\r\n    }\r\n}\r\n```\r\n\r\n**Unsafety 1**\r\nIf the type of the domain of the mapper function does not match the 'real' type, then a ClassCastException will occur at runtime.\r\n\r\n**Unsafety 2**\r\ntreeMap does cast the result via generic method parameter\r\n\r\n**Thoughts**\r\nBeing unsafe is a fair balance between _type safety_ and _viability_. E.g. `Monad.flatten()` works in a similar unsafe way. It assumes that the `Monad<T>` is of the form `Monad<? extends Monad<U>>`. Also `Traversable.{min,max,sum,product,...}` assume s.th. about the component type and may throw at runtime.\r\n\r\n```java\r\n// Adaption of your example\r\npublic class Issue185Test {\r\n\r\n    static Option<String> extract(String str) {\r\n        if (str.length() >= 15) {\r\n            return Option.of(str.substring(10, 15));\r\n        } else {\r\n            return Option.none();\r\n        }\r\n    }\r\n\r\n    @Test\r\n    public void shouldTreeMapStream() {\r\n\r\n       final Stream<Option<Long>> sums = Stream.of(\"cocotero\", \"chuchu blabla pinpon\")\r\n                .map(Issue185Test::extract)\r\n                .treeMap(String::chars)\r\n                .treeMap(IntStream::sum);\r\n\r\n        // sums = Stream(None, Some(447))\r\n    }\r\n}\r\n```\r\n\r\nI really love this issue. Especially it shows me where I need to tweak the Javaslang API a little bit, i.e. it shows that in addition to `Match` a `CheckedMatch` is needed because we also have a `CheckedMonad` which is implemented by `Try`. There we deal with checked functions.\r\n\r\nAfter adding the missing parts and polishing the code, this will go into release 1.2.1."}}