Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error occurs when apply first into empty list #275

Closed
boathit opened this issue Sep 30, 2015 · 8 comments
Closed

Error occurs when apply first into empty list #275

boathit opened this issue Sep 30, 2015 · 8 comments

Comments

@boathit
Copy link

boathit commented Sep 30, 2015

Both first([]) and first('') will throw an error, should it be more reasonable to return a None when the list is empty?

@llllllllll
Copy link
Contributor

Returning a None is problematic because None is a valid element of the codomain of first. How do you distinuish between first((None,)) and first(())?

@boathit
Copy link
Author

boathit commented Sep 30, 2015

It indeed is an awkward case in Python, in Clojure since nil is a valid empty list then both first '() and first '(nil) can comfortably return nil, anyway throwing an error is really frustrating

@llllllllll
Copy link
Contributor

I have a pr here to make handling exceptions easier: #262
Does this fit your use case to use: excepts(StopIteration, first) instead of first? This returns a function that automatically wraps the try/except for you.

@eriknw
Copy link
Member

eriknw commented Sep 30, 2015

You're not the first one coming from Clojure to find the usage of first confusing or frustrating. See #247. The "value" of a missing element in Python is not None, it is an exception that is raised. This is indeed a good use case for @llllllllll's excepts PR to allow a user to easily create functions to behave the way you want.

@boathit, would first = excepts(StopIteration, first) be an acceptable solution for you, or do you still think first, second, nth, and last should always return None even if there aren't enough elements in the sequence? It's too bad Python doesn't have a Missing singleton...

@boathit
Copy link
Author

boathit commented Oct 1, 2015

Yeah, it would be helpful, thanks. Additionally, the suggestion that defining a Nil singleton in toolz sounds really attractive, I would much more prefer it if it can be achieved.

@llllllllll
Copy link
Contributor

Maybe I am biased from other languages but I think that there is no singleton value you can define that will not be ambigious. Nil or None have the same problem here, what should the return of first [Nil] be? How do I know when this is empty? I think that unless we explicitly use option types, the only way we can resolve this ambiguity is with a different kind of return value like an exception.

$ ghci
λ> head []
*** Exception: Prelude.head: empty list
λ> import Data.Maybe (listToMaybe)
λ> listToMaybe []
Nothing
λ> listToMaybe [1]
Just 1
$ sml
- hd [];
stdIn:1.2-1.7 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)

uncaught exception Empty
  raised at: smlnj/init/pervasive.sml:209.19-209.24
- open List;
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[autoloading done]
opening List
  datatype 'a list = :: of 'a * 'a list | nil
  exception Empty
  val null : 'a list -> bool
  val hd : 'a list -> 'a
  val tl : 'a list -> 'a list
  val last : 'a list -> 'a
  val getItem : 'a list -> ('a * 'a list) option
  val nth : 'a list * int -> 'a
  val take : 'a list * int -> 'a list
  val drop : 'a list * int -> 'a list
  val length : 'a list -> int
  val rev : 'a list -> 'a list
  val @ : 'a list * 'a list -> 'a list
  val concat : 'a list list -> 'a list
  val revAppend : 'a list * 'a list -> 'a list
  val app : ('a -> unit) -> 'a list -> unit
  val map : ('a -> 'b) -> 'a list -> 'b list
  val mapPartial : ('a -> 'b option) -> 'a list -> 'b list
  val find : ('a -> bool) -> 'a list -> 'a option
  val filter : ('a -> bool) -> 'a list -> 'a list
  val partition : ('a -> bool) -> 'a list -> 'a list * 'a list
  val foldr : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
  val foldl : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
  val exists : ('a -> bool) -> 'a list -> bool
  val all : ('a -> bool) -> 'a list -> bool
  val tabulate : int * (int -> 'a) -> 'a list
  val collate : ('a * 'a -> order) -> 'a list * 'a list -> order
- getItem [];
stdIn:2.1-2.11 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)
val it = NONE : (?.X1 * ?.X1 list) option
- getItem [1];
val it = SOME (1,[]) : (int * int list) option

@mrocklin
Copy link
Member

mrocklin commented Oct 1, 2015

I agree with @llllllllll here. I think that it's idiomatic in Python to get a StopIteration or IndexError error in this case.

The Python language doesn't have such a singleton and I think that it's beyond scope for toolz to create one.

@boathit
Copy link
Author

boathit commented Oct 2, 2015

Well, thanks you guys insightful argument.

@boathit boathit closed this as completed Oct 2, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants