Sometimes it really cleans up code to pull out and name subpieces. (In some ways this really is the Haskell way to comment code)
This is wordier that what you did above, but I think it is much easier to understand....
First I start with some definitions:
type Info=([Int], S.Set Int) --This is the remaining and seen items at a point in the list
item=head . fst --The current item
rest=fst --Future items
seen=snd --The items already seen
Then I add two self descriptive helper functions:
itemHasBeenSeen::Info->Bool
itemHasBeenSeen info = item info `S.member` seen info
moveItemToSet::Info->Info
moveItemToSet info = (tail $ rest info, item info `S.insert` seen info)
With this the program becomes:
nub2::[Int]->[Int]
nub2 theList =
map item
$ filter (not . itemHasBeenSeen)
$ takeWhile (not . null . rest)
$ iterate moveItemToSet start
where start = (theList, S.empty)
Reading from bottom to top (just as the data flows), you can easily see what it happening:
start=(theList, S.empty), start with the full list, and an empty set.
iterate moveItemToSet start, repeatedly move the first item of the list into the set, saving each iteration of Info in an array.
takeWhile (not . null . rest)- Stop the iteration when you run out of elements.
filter (not . itemHasBeenSeen)- Remove items that have already been seen.
map item- Throw away the helper values....
g = go Set.empty where go _ [] = []; go s (x:xs) = if Set.member x s then go s xs else x:go (Set.insert x s) xsscanl, this works:mapMaybe fst . scanl (\(_, st) e -> if Set.member e st then (Nothing, st) else (Just e, Set.insert e st)) (Nothing, Set.empty). It's also similar to how you might useunfoldr.g x = concat $ unfoldr go (Set.empty, x) where go (_,[]) = Nothing; go (s,(x:xs)) = Just (if Set.member x s then [] else [x], (Set.insert x s, xs))