madlib-lang / madlib

Madlib language compiler
https://madlib.space
BSD 3-Clause "New" or "Revised" License
29 stars 1 forks source link

Confusing behavior with conditional `split` / `of` #98

Closed brekk closed 1 year ago

brekk commented 1 year ago
import { when,ifElse, always, equals, complement, all } from "Function"
import { lt } from "Compare"
import { trim, split, join } from "String"
import { length, sortBy, find, includes, uniqueBy, reduce, concat } from "List"
import type {Either} from "Either"
import type {Maybe} from "Maybe"
import { Left, Right } from "Either"
import { splitEvery } from "./List"
import IO from "IO"

export type MargeCommand flagmap input
  = Command(flagmap, input)
  | InvalidCommand(flagmap, input)
  | UnknownCommand

export type CommandFlag name aliases parseType special
  = Flag(name, aliases, parseType, special)

export type MargeConfig flagmap = MargeConfig(flagmap)

export type ParseType
  = ParseNumber
  | ParseBoolean
  | ParseString

export type SpecialType
  = Exclusive
  | Accumulative
  | Unspecial

export alias MargeFlag = CommandFlag String (List String) ParseType SpecialType

alias MargeFlags = List MargeFlag
alias ReadFlags = #[ MargeFlags, MargeFlags ]

getAllFlags :: MargeFlags -> List String
export getAllFlags = pipe(
  reduce((agg, x) => where (x) {
    Flag(name, aliases, _, _) => concat(agg, concat(aliases, [name]))
  }, []),
  uniqueBy(equals)
)

hasFlag :: String -> MargeFlags -> Boolean
export hasFlag = (x, flags) => pipe(
  getAllFlags,
  includes(x)
)(flags)

findNormalizedFlag :: String -> MargeFlags -> Maybe MargeFlag
export findNormalizedFlag = (x, flags) => find(
  where {
    Flag(name, aliases, _, _) => equals(name, x) || includes(x, aliases)
  }
)(flags)

excludes :: Eq a => a -> List a -> Boolean
excludes = (a, xs) => !includes(a, xs)

validateFlag :: MargeFlags -> MargeFlag -> Boolean
export validateFlag = (flags, flag) => pipe(
  getAllFlags,
  (rawFlags) => where (flag) {
    Flag(name, aliases, t, s) => (
      excludes(name, aliases) && excludes(name, rawFlags) && all(excludes($, rawFlags), aliases)
    )
  }
)(flags)

validateAllFlags :: MargeFlags -> Boolean
export validateAllFlags = (list) => where (list) {
  [] => true
  [x, ...xs] => validateFlag(xs, x) && validateAllFlags(xs)
}

readFlags :: MargeFlags -> ReadFlags
export readFlags = (list) => pipe(
  reduce(
    (agg, flag) => where (agg) {
      #[invalid, valid] => do {
        isValid = validateFlag(valid, flag)
        return #[
          !isValid ? concat(invalid, [flag]) : invalid,
          isValid ? concat(valid, [flag]) : valid,
        ]
      }
    },
    #[[], []]
  )
)(list)

readInvalidFlags :: ReadFlags -> Boolean
export readInvalidFlags = (
  where {
    #[invalid, valid] => pipe(length, lt(0))(invalid)
  }
)

hasInvalidFlags :: MargeFlags -> Boolean
export hasInvalidFlags = pipe(
  readFlags,
  readInvalidFlags
)

safeReadFlags :: MargeFlags -> #[ Either (MargeFlags) (MargeFlags), Either (MargeFlags) (MargeFlags) ]
export safeReadFlags = pipe(
  readFlags,
  where {
    #[invalid, valid] => #[Left(invalid), Right(valid)]
  }
)

lengthSort = sortBy((a, b) => compare(length(a), length(b)))

summarizeFlag :: MargeFlag -> String
export summarizeFlag = where {
  Flag(name, aliases, _, _) => `Flag(${name}::${join(":", aliases)})`
}

generateFlagObject :: MargeFlags -> List #[String, String]
generateFlagObject = reduce((agg, flag) => where (flag) {
  Flag(name, aliases, _, _) => concat(agg, [#[name, ""]])
}, [])

// parsify :: String -> MargeFlags -> List #[String, a]
parsify = (input, flags) => pipe(
  // split(#- /[\s\=]/g -#),
  split(" "),
  map(trim),
  map(ifElse(includes("="), split("="), of)), // <-- issue is here
  // map(of), // fails with "No instance for 'Monad {}' was found.
  // map(split("=")), // works
  // splitEvery(2),
  IO.trace("what?"),
  always(input)
)(input)

parser :: MargeFlags -> String -> String
export parser = (conf, input) => pipe(
  readFlags,
  ifElse(
    readInvalidFlags,
    where {
      #[bad, _] => `Invalid:\n  - ${pipe(map(summarizeFlag), join("\n  - "))(bad)}`
    },
    where {
      #[_, good] => pipe(
        IO.trace(`MargeConfig({\n  flags: [\n    ${pipe(map(summarizeFlag), join(",\n    "))(good)}\n  ]\n})`),
        parsify(input),
        IO.trace("gen gen"),
        always("temp temp")
      )(good)
    },
  ),
  IO.trace("normcore flag"),
)(conf)

This results in an error like:

[error]: Type error
     ╭──▶ /madness/marge/src/Marge.mad@130:29-130:39
     │
 130 │   map(ifElse(includes("="), split("="), of)),
     •                             ┬─────────
     •                             ╰╸ expected:
     •                                  List String -> List String
     •
     •                                but found:
     •                                  String -> List String
     •
─────╯

Symptoms:

  1. What did you expect to happen? AFAIK of and split('xxxxxxxxx') should behave the same
  2. What actually happened? The error seems to suggest I have the wrong types, but map(split("=")) works correctly
aboeglin commented 1 year ago

@brekk From what I remember the issue was the origin of includes. You used the one from List instead of the one from String and that led to the type error. Can we close it?

brekk commented 1 year ago

Yeah, this can be closed now