News for hackers. Stuff that matters.
Re:Understanding contravariance by tome on Friday June 22, 2018 @09:57AM (#17373974)
> co/contravariant representations represent exactly the point at which formal typesafety starts hurting instead of helping as an engineering practice.

I can't understand what you mean by this. The Python 'list' type is covariant in its contents and comparison functions such as 'operator.lt' are contravariant in their arguments regardless of whether Python has types or not.

Co- and contravariance just are. They're not things that dynamic languages somehow don't have. They do have them! It's just harder to see them (and harder to take advantage of them for your own benefit).

Re:Understanding contravariance by a1369209993 on Friday June 22, 2018 @02:01PM (#17375975)
> The Python 'list' type is covariant in its contents

  l1 = [0,1,1,0] # list(unsigned)
  l2 = l2 # list(int) includes all list(unsigned)
  l2[1] = -42 # error: l1 is no longer all unsigned
Mutatable references are pretty much never covariant. Immutable containers like `tuple` might be, but/because there's no support for appending to or otherwise modifying them.
Re:Understanding contravariance by tome on Saturday June 23, 2018 @01:39AM (#17379723)
A mutable array in Haskell is most certainly covariant so you may have to rethink your counterexample.
Re:Understanding contravariance by a1369209993 on Sunday June 24, 2018 @03:15AM (#17385400)
Haskell's Data.Array.IO IOArrays (which I assume you're talking about) are not covariant; they are invariant, with a very nice type inference system to paper over this in most cases.

  {-# OPTIONS_GHC -W -XRankNTypes -XImpredicativeTypes -XFlexibleContexts #-}
  import Data.Array.IO
  
  type Subtype = (forall a . a->Int)
  type Suptype = (Int -> Int)
  
  esub = (\_ -> 0) :: Subtype
  esup = (\x -> x+1) :: Suptype
  wantsub = (\f -> f 'a') :: Subtype -> Int
  wantsup = (\f -> f 0) :: Suptype -> Int
  
  writeArraySup :: IOArray Int Suptype -> IO ()
  writeArraySup a = writeArray a 1 esup
  
  main = do asub <- newArray (1,1) esub
            writeArraySup asub
            a1sub <- readArray asub 1
            print $ wantsub a1sub
In the above code, asub can be either (ie, not both) `IOArray Int Suptype` (in which case wantsub will fail to typecheck) or `IOArray Int Subtype`, in which case writeArraySup will fail to typecheck, because A'Sub isn't actually a subtype of A'Sup, because IOArray Int isn't actually covariant. You can see, I hope, that if they were covariant, and writeArraySup did typecheck, then we would end up passing 'a' (a Char) into (+) (expects Int) in violation of type safety.

If you meant some other mutable array type, then the same reasoning applies, but I can't be arsed to work out another concise example.

Re:Understanding contravariance by tome on Sunday June 24, 2018 @12:34PM (#17387139)
That's not the sort of variance that was discussed in the article, where an explicit mapping function is applied, not an implicit subtying relationship.
Re:Understanding contravariance by ufo on Friday June 22, 2018 @04:44PM (#17377183)
Some languages intentionally have an unsound type system in respect to variance, and rely on runtime checks for safety. It is a facet of the old static typing vs dynamic typing tradeoff.