LightAndLight / ipso

A functional scripting language.
https://ipso.dev
17 stars 1 forks source link

Runtime error due to type mismatch #326

Closed LightAndLight closed 1 year ago

LightAndLight commented 1 year ago

Program:

rebase.ipso:

#! /usr/bin/env ipso

startswith : String -> String -> Bool
startswith prefix value =
  let split = string.split prefix value in
  array.length split > 0 && array.index 0 split == ""

filter : (a -> Bool) -> Array a -> Array a
filter predicate items =
  array.unfoldr 
    0 
    (\seed -> 
      if seed >= array.length items
      then Done ()
      else 
        let item = array.index seed items in
        if predicate item
        then Step { value = item, next = seed + 1 }
        else Skip { next = seed + 1 }
    )

not : Bool -> Bool
not b = if b then false else true

find : (a -> Bool) -> Array a -> (| Found : Int, Missing : () |)
find predicate items =
  (array.foldl 
    (\acc item ->
      case acc.result of
        Missing () ->
          if predicate item
          then { index = acc.index, result = Found acc.index }
          else { index = acc.index + 1, result = acc.result }
        Found _ ->
          acc
      )
    { index = 0, result = Missing () }
    items
  ).result

swap : Int -> Int -> Array a -> Array a
swap m n items =
  array.generate
    (array.length items)
    (\index ->
      array.index
        (if index == m then n else if index == n then m else index)
        items
    )

parseLine : String -> { action : String, commit : String }
parseLine line =
  let parts = string.partsc ' ' line in
  # let [action, parts, .._] = string.partsc ' ' line in
  { action = array.index 0 parts, commit = array.index 1 parts }

main : IO ()
main =
  comp
    bind args <- env.args
    let commit1 = array.index 0 args
    let commit2 = array.index 1 args
    let rebaseFile = array.index 2 args
    # let [commit1, commit2, file] = args in

    bind content <- file.read rebaseFile
    let entries = array.map parseLine <| filter (\line -> not (startswith "#" line)) <| string.partsc '\n' content

    let commit1Found = find (\entry -> entry.commit == commit1) entries 
    println <| debug commit1Found
    let commit2Found = find (\entry -> entry.commit == commit2) entries
    println <| debug commit2Found

    bind swappedEntries <-
      case commit1Found of
        Missing () -> exit.failure
        Found commit1Index ->
          case commit2Found of
            Missing () -> exit.failure
            Found commit2Index ->
              comp
                println <| debug entries
                println <| debug <| swap commit1Index commit2Index entries
                io.pure <| swap commit1Index commit2Index entries

    println <| debug entries
    println <| debug swappedEntries

Output:

$ cat z 
a x
b x
c x

$ ./rebase.ipso x x z
Found 0
Found 0
[{ action = "a", commit = "x" }, { action = "b", commit = "x" }, { action = "c", commit = "x" }]
[{ action = "a", commit = "x" }, { action = "b", commit = "x" }, { action = "c", commit = "x" }]
thread 'main' panicked at 'expected array, got Variant(0, Int(0))', src/lib.rs:242:20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
LightAndLight commented 1 year ago

I found another crash. Here's a smaller reproduction:

test.ipso

#! /usr/bin/env ipso

main : IO ()
main = 
  let predicate = \item -> false in
  let items = ["a", "b", "c"] in
  println <| debug
    (array.foldl 
      (\{ index, result } item ->
        let nextIndex = index + 1 in
        case result of
          Found _ -> 
            { index = nextIndex, result = result}
          Missing () ->
            if predicate item
            then { index = nextIndex, result = Found index }
            else { index = nextIndex, result = result }
      )
      { index = 0, result = Missing () }
      items
    ).result
$ ./test.ipso 
thread 'main' panicked at 'expected closure, got Array([Object(String("a")), Object(String("b")), Object(String("c"))])', src/lib.rs:311:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Is it just coincidence that both these involve an implementation of find using array.foldl?

LightAndLight commented 1 year ago

When I change { index, result } to acc and use field access on acc, the program runs correctly:

#! /usr/bin/env ipso

main : IO ()
main = 
  let predicate = \item -> false in
  let items = ["a", "b", "c"] in
  println <| debug
    (array.foldl 
      (\acc item ->
        let nextIndex = acc.index + 1 in
        case acc.result of
          Found _ -> 
            { index = nextIndex, result = acc.result }
          Missing () ->
            if predicate item
            then { index = nextIndex, result = Found acc.index }
            else { index = nextIndex, result = acc.result }
      )
      { index = 0, result = Missing () }
      items
    ).result
LightAndLight commented 1 year ago

https://github.com/LightAndLight/ipso/issues/326#issuecomment-1429068614: When I factor predicate out into a top level definition, the code works correctly. I think that comment is a duplicate of https://github.com/LightAndLight/ipso/issues/357, at least.

LightAndLight commented 1 year ago

Fixed by https://github.com/LightAndLight/ipso/pull/361.