Search code examples
smlsmlnj

Why does "not" care about "List.exists" input in SML?


I made a function that identifies if a certain object has repetition as following: (PS: test is passing)

(* ListOfPattern -> Pattern -> String List -> List.exists (function) ->  bool*)
val check_pat =
  let 
    fun patternToVars p =
      case p of
        Variable x => [x]
      | TupleP lop => List.foldl (fn (p, acc) => (patternToVars p) @ acc) [] lop
      | _ => []
        
    fun isDuplicated lov =
      case lov of
        [] => false
      | lov::lov' => List.exists (fn prevString => prevString = lov) lov' orelse isDuplicated lov'
  in
    not o isDuplicated o patternToVars 
    (* Cannot understand why `not o` works but `not` does not, duplicate returns bool in all 
        cases and there is no need for piping *)
  end

But if I remove o from not o isDuplicated o patternToVars and make it not isDuplicated o patternToVars it returns the following error:

Error: operator and operand don't agree [tycon mismatch]
  operator domain: bool
  operand:         ''Z list -> bool
  in expression:
    not isDuplicated

And the question is "why isDuplicated returning a list?"


Solution

  • The message does not say that isDuplicated returns a list, it says that isDuplicated is a function with the type ''Z list -> bool, and that you're trying to apply not to it when not requires a bool argument.

    not o is not a meaningful "unit"; o is a binary operator, and f o g is equivalent to fn x => f (g x).

    Because function application has higher precedence than o,

    not isDuplicated o patternToVars
    

    is equivalent to

    (not isDuplicated) o patternToVars
    

    That is, you're trying to negate the function isDuplicated, not its result.

    Perhaps your particular issue becomes more obvious if you add the argument and nest explicit function applications; the first is equivalent to

    fn p => not (isDuplicated (patternVars p))
    

    while the second is equivalent to

    fn p => (not isDuplicated) (patternVars p)
    

    which you can see is very different.


    Another attempt to clarify:

    Perhaps it becomes clearer if we define composition as a function instead of as an infix operator:

    fun compose (f, g) = fn x => f (g x)
    

    or even

    fun compose (f, g) = f o g
    

    Then,

    not o isDuplicated o patternToVars
    

    is the same as

    compose (not, compose (isDuplicated, patternToVars))
    

    while

    not isDuplicated o patternToVars
    

    is the same as

    compose (not isDuplicated, patternToVars)