Stream: beginners

Topic: Self-referential type problem !


view this post on Zulip Artur Swiderski (Nov 08 2023 at 13:20):

Why this code does not work and how to implement equivalent

modifyActive = \ head ->

    updatedLst =

        if List.isEmpty head.children == Bool.true then
            [head]
        else
            head.children
    updatedLst




a = { children :  []}
kk =modifyActive a

view this post on Zulip Artur Swiderski (Nov 08 2023 at 13:39):

tree like are rather quite basic structures how to accomplish creating them in Roc

view this post on Zulip Artur Swiderski (Nov 08 2023 at 16:39):

I think I can workaround this problem using Tags a = { children : Childern []} <-- something like that

view this post on Zulip Artur Swiderski (Nov 08 2023 at 16:40):

modifyActive = \ head ->

    updatedLst =
         when  head.children is
           Ok children ->
                    if List.isEmpty head.children == Bool.true then
                          [head]
                     else
                           head.children
            Err ->  []
    updatedLst

view this post on Zulip Anton (Nov 08 2023 at 16:58):

This would definitely be something good to add to the examples.
I think with tags you'll still encounter self-referential issues later on.
You could go with something like this:

NodeID : U64

Node : { value: Str, children :  List NodeID}

tree : Dict NodeID Node
tree = Dict.empty {}
       |> Dict.insert 0 { value: "hey", children: [1]}
       |> Dict.insert 1 { value: "hey!", children: []}

This particular code assumes you want to store a Str in each Node.

view this post on Zulip Anton (Nov 08 2023 at 17:07):

If you don't want to store anything in the Node, you could go with this:

NodeID : U64

Children : List NodeID

tree : Dict NodeID Children
tree = Dict.empty {}
       |> Dict.insert 0 [1]
       |> Dict.insert 1 []

view this post on Zulip Artur Swiderski (Nov 08 2023 at 17:38):

thx I just encounter problems apparently what I am trying is impossible, but your solution will work for sure

view this post on Zulip Artur Swiderski (Nov 08 2023 at 17:42):

btw, this crashes compiler

modifyActivetest = \ head ->
    updatedLst =
        when head.children is
            Children  children->
                if List.isEmpty children == Bool.true then
                    [{ locked : Bool.false, children : Children [], value : [] }]
                else
                    when List.last children is
                        Ok elem ->
                            if elem.locked == Bool.true then
                                [{ locked : Bool.false, children : Children [], value : [] }]
                            else
                                [modifyActivetest elem]
                        Err  _ -> []
            _ -> []

    { locked : head.locked, children : Children updatedLst , value : head.value }



    www = { locked : Bool.false, children : Children [], value : [] }

    uuu =  modifyActivetest  www

view this post on Zulip Brendan Hansknecht (Nov 08 2023 at 17:49):

I think we have a few bugs to file here and track.

  1. Recursion though lists/dicts/etc instead of tags should just work.
  2. We already have a super old bug for this, but the recursive tag definition issue if they aren't in a single definition.
  3. The crash is probably a separate issue and should be filed as well.

view this post on Zulip Artur Swiderski (Nov 08 2023 at 18:40):

Brendan Hansknecht said:

I think we have a few bugs to file here and track.

  1. Recursion though lists/dicts/etc instead of tags should just work.
  2. We already have a super old bug for this, but the recursive tag definition issue if they aren't in a single definition.
  3. The crash is probably a separate issue and should be filed as well.

It turned out that through tags I was not able to accomplish anything as well, it fall apart when I tried do something useful in real life

view this post on Zulip Artur Swiderski (Nov 08 2023 at 18:50):

In terms of language adaptation, as new user intuitively I would rather expect that language (any language) will figure out that what I am trying to do is perfectly fine. But still, if feature is missing (for technical or other reasons) I can live with this Dict approach presented by Anton

view this post on Zulip Brendan Hansknecht (Nov 08 2023 at 19:52):

Yeah, I am not sure about the technical problems here, but I think it should just work. Gonna chat with others.

view this post on Zulip Brendan Hansknecht (Nov 08 2023 at 20:02):

@Artur Swiderski This should work and be a lot nicer than dictionaries:

Node : [Branch (List Node)]

modifyActive : Node -> List Node
modifyActive = \head ->
    Branch children = head
    if List.isEmpty children then
        [head]
    else
        children

view this post on Zulip Artur Swiderski (Nov 08 2023 at 20:36):

the problem is that my code is sightly bit more complex then example I provided
my target function is

modifyActive : Node -> Node
not
modifyActive : Node -> List Node (this worked in some variations in other it crashed but not self reference problem)

the type I used was: Node : [ { branch : Branch (List Node), ... } ]

I have to check if this works with
Node : [ Element { branch : List Node, ... } ] ?? (I will check my code with this type used)

So to repeat :
Earlier
I fall into problems when I tried (this is equivalent code):
With type like this :
Node : [ { branch : Branch (List Node), ... } ]
I tried something like this :

modifyActive : Node ->  Node
modifyActive = \head ->
    Branch children = head
    updatedList =
    if List.isEmpty children then
        [ ----> create new  node  element but no problem  here  <---]
    else
        when List.last children  is
               Ok  child ->
 #  before I checked  if child  is eligible for  modification  but  here  it is irrelevant
                     List.dropLast children
                     |> List.append (  modifyActive child )     <--------in  this  line it failed with reference  to itself  problem
....
{ head &  branch : updatedList }  <-- function has this  as  return  value

view this post on Zulip Brendan Hansknecht (Nov 08 2023 at 20:48):

I think we are just giving you a misleading type error here. This code checks for me and should be similar to your goal above:

modifyActive : Node -> Node
modifyActive = \head ->
    Branch children = head
    when List.last children is
        Ok child ->
            List.dropLast children 1
            |> List.append (modifyActive child)
            |> Branch # This converts back from (List Node) to a Node
        Err _ ->
            # This is the list empty case, make new node or etc.
            head

view this post on Zulip Artur Swiderski (Nov 08 2023 at 21:06):

But I explicitly recover object in line
{ head & branch : Branch updatedList } <--- that was last line of my function

I will check tomorrow but keep in mind that I need different type. then you are using
something like:
Node : [ { branch : (List Node), ... } ] <--- this checked already and it failed on reference to itself
Node : [ { branch : Branch (List Node), ... } ] <--- this checked already and it failed on reference to itself
Node : [ Element { branch : (List Node), ... } ] <--- maybe this will work
Node : [Element { branch : Branch (List Node), ... } ] <--- or this

I need Node to be record (and maybe record usage is the problem ??)

I will try two last options in case of problems , Dict approach will work for sure

view this post on Zulip Artur Swiderski (Nov 08 2023 at 21:10):

I am using like 2 weeks old Roc so maybe that's the problem I will update environment

view this post on Zulip Brendan Hansknecht (Nov 08 2023 at 21:43):

I would expect Node: [Element { branch: List Node, ...}] to work with current roc.

view this post on Zulip Brendan Hansknecht (Nov 08 2023 at 21:43):

Also, I'll try to fix the base roc issue.

view this post on Zulip Artur Swiderski (Nov 09 2023 at 15:22):

this may be the same problem as I mentioned before but this piece of code compiles but crashes

modifyActive = \ op, currentStructHead  ->
    Node  head = currentStructHead

    updatedLst =
        if List.isEmpty head.children == Bool.true then
            op head.children
        else
            when List.last head.children is
                Ok node ->
                    Node child = node
                        if child.locked == Bool.true then
                            op head.children
                        else
                            modifyLastInList head.children  (modifyActive op  node )

                Err _ -> []

    Node {head & children : updatedLst }


main =

    www = Node { locked : Bool.false, children :  [], value : [] }
    fun1 = (\ lst -> List.append lst (Node { locked : Bool.false, children : [], value : [] } ))

    wataC = modifyActive  fun1  www


    .
    .
    .

view this post on Zulip Artur Swiderski (Nov 09 2023 at 15:23):

I would rather consider it valid, or at least it would be nice to get some meaningful message out of execution if it is not

view this post on Zulip Artur Swiderski (Nov 09 2023 at 15:34):

I placed it here https://github.com/roc-lang/roc/issues/5970


Last updated: Jul 06 2025 at 12:14 UTC