4

Write a function remove_option, which takes a string and a string list. Return NONE if the string is not in the list, else return SOME xs where xs is identical to the argument list except the string is not in it. You may assume the string is in the list at most once. Use same_string, provided to you, to compare strings. Sample solution is around 8 lines.

The function type should be fn : string * string list -> string list option.Here is my code

fun same_string(s1 : string, s2 : string) =
    s1 = s2
fun remove_option (str: string ,str_list : string list) =
    case str_list of 
        [] => NONE
          | x::xs => if same_string(x,str) 
             then SOME xs 
             else x :: remove_option( str,xs)

and the error report

hw2provided.sml:10.5-15.37 Error: right-hand-side of clause doesn't agree with f
unction result type [tycon mismatch]
  expression:  _ option
  result type:  string list
  in declaration:
    remove_option =
      (fn (<pat> : string,<pat> : string list) =>
            (case str_list
              of <pat> => <exp>
               | <pat> => <exp>))

uncaught exception Error
  raised at: ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
             ../compiler/TopLevel/interact/evalloop.sml:44.55
             ../compiler/TopLevel/interact/evalloop.sml:292.17-292.20

So where is the bug ?

Slark
  • 43
  • 4

2 Answers2

5

The problem is that you want to return a string list option but the line

else x :: remove_option( str,xs)

makes it seem that you want to return a string list

What you need to do with the return value of remove_option( str,xs) is

1) decide what to do if it is NONE

2) extract the string list strings (or whatever you want to call it) if it is of the form SOME strings, tack x onto the front of the list, and repackage it with SOME before returning it.

You seem comfortable with the use of case, so you could use it here.

John Coleman
  • 51,337
  • 7
  • 54
  • 119
3

Since John showed where the bug is, here are some extra comments:

  • Since the function same_string is not injected, it is superfluous. You might as well use =.
  • Recursive functions that return 'a option are kind of tricky, since you need to unpack the result:

    fun remove_option (s1, []) = NONE
      | remove_option (s1, s2::ss) =
        if s1 = s2
        then SOME ss
        else case remove_option (s1, ss) of
                  NONE => NONE
                | SOME ss' => SOME (s2::ss')
    

    Generally, when you see the pattern

    case x_opt of
         NONE => NONE
       | SOME x => SOME (f x))
    

    this can be refactored into using e.g. Option.map : ('a -> 'b) -> 'a option -> 'b option:

    Option.map f x_opt
    

    In this case,

    fun curry f x y = f (x, y)
    
    fun remove_option (s1, []) = NONE
      | remove_option (s1, s2::ss) =
        if s1 = s2
        then SOME ss
        else Option.map (curry op:: s2) (remove_option (s1, ss))
    

    where curry op:: s2, the function that puts s2 in front of a list.

sshine
  • 15,635
  • 1
  • 41
  • 66
  • What do you mean by "injected"? I kind of get it intuitively, based on the code snippet, but seems to have a more technical meaning here. – Ionuț G. Stan Apr 25 '16 at 13:43
  • It probably just means what you think it means. If `remove_option` took a function `string * string -> bool` as argument, then *'a* would not need to be an equality type. – sshine Apr 25 '16 at 13:48
  • 1
    Oh, ok, so it was injected as in passed as an argument. Thanks. I thought you meant something similar to the `inject` here: http://mlton.org/UniversalType – Ionuț G. Stan Apr 25 '16 at 13:55
  • @IonuțG.Stan: No, but thanks for linking - it is an interesting read. I thought of a similar example of reference use this weekend: `entangle : 'a -> (unit -> 'a) * ('a -> unit)`. I'm not sure it's a good pattern, but it seemed fun. – sshine Apr 25 '16 at 17:59
  • Thank you very much for your answer.Your answer reinforces my understanding of type option!!! From your answer I realized that I did not have a very good understanding of option concept before.And you give your refactored solution,it is well for me to think this problem form functional programming .Again,thanks a lot for spending time writing this answer!!! – Slark Apr 26 '16 at 02:13
  • 1
    @Slark on StackOverflow, it's customary to mark one of the answers as accepted. There's a small tick sign below the upvote and downvote buttons. Use that to mark an answer as accepted. – Ionuț G. Stan Apr 26 '16 at 06:48
  • It is also not customary to ask for this yourself, as it makes one seem like a petty point collector, so thanks for pointing it out, @IonuțG.Stan. :-D – sshine Apr 26 '16 at 09:10
  • @IonuțG.Stan. thank you for pointing it out,it is my first use .And I have done that – Slark Apr 26 '16 at 14:26