1. 1

    This makes me wonder what blessed syntax would be for a positive assertion equivalent of an if-statement would be. ensure timer != null has a taste to it…

    1. 2

      Might place more requirements on both lang and lib than you were asking for, but I’ve been messing with a reterr (return error) keyword in my language Tiko that works alot like Rust’s try! macro/? operator.

      Tiko uses an Option instead of null, so

      func doThing(x Option<T>) {
          reterr x
          fmt.Println("only reached if not null!")
      }
      

      desugars into

      func doThing(x Option<T>) {
          match x {
            None => return
          }
      
          fmt.Println("only reached if not null")
      }
      

      reterr returns on any falsey expression, any Result<T, E> that is an Error, and any empty Option<T>. Anything else continues through. It will also pass the error up from Result iff the func’s type signature returns an Error.

      I like it alot. It makes for some VERY clean code that reads declaratively but doesn’t seem out of place in the imperative style. Preconditions and Go-like if err != nil { return err } handling are readable but don’t take up the majority of visual space on screen.

      I worry because it does quite alot of things. It blesses Result and Option, plus it just happens to work with falsey expressions which aren’t necessarily errors. It’s got utility, but not exactly the greatest in terms of aesthetics from a language design standpoint. reterr and return are also super similar visually and might be confused/missed. Since this is mainly a toy language it’s mostly just me having fun and I haven’t had much issue with it so maybe I’m just too hesitant to commit ;)

      It’s definitely not a new idea (mostly ripped off from rejected ideas in the Rust ? operator RFC) but given your comment I thought it might be interesting enough to merit a reply.

      1. 2

        That’s rad! cheers! I imagine it might end up looking a little Eifel like eventually with a neat stack of precondition reterrs at the beginning.

      2. 2

        Some languages (like lisps/schemes) have when and unless. (when foo bar ...) is similar to if (foo) { bar ... }, (unless foo bar ...) is like if (foo) {} else { bar ... }.

        1. 2

          Yeah, I’m solidly on the “unless is a useful keyword camp” in Ruby land. I kinda want something that combines that with a return or raise or something though… /u/badtuple has some neat thoughts below.

        2. 2

          ensure is probably one of the better terms to use for this. I might steal this!

          1. 3

            Another one you might like is require for preconditions.

            1. 2

              require for preconditions and ensure for postconditions is lovely!

              1. 1

                I think require may be ambiguous in JS land :)

            2. 2

              I personally like unless keyword, though unfortunately it’s not present in Java. Also one I really liked (maybe most) is a pattern-matching-like approach I saw in Erlang:

              execute_timer(entity) when entity /= null ->
                  sound_alarm(entity);
              execute_timer(_) ->
                  do_nothing.
              
              1. 2

                Ah yeah, I’m most familiar with pattern matching in Scala and agree it’s pretty rad, but the Erlang way seems better in some ways since it groups cases together from a definition standpoint (?). I think Haskell does that too, and it makes for really clean stuff there.

              2. 1

                Swift has the guard statement.

                Here is a rough translation of the function in the linked article.

                func run(timer : Timer) throws
                {
                    guard timer.enabled else {
                        return
                    }
                    guard timer.valid else {
                        throw InvalidTimerException()
                    }
                    timer.run()
                }
                
              1. 1

                “Type theory is a theory of computation that classifies programs according to their behavior, rather than their structure. Types are themselves programs whose values stand for specifications of program equivalence.”

                I had to stop right there. Most of us in imperative languages using types use them or structure. Especially in terms of structs or objects. This says types have nothing to do with structure. Makes me wonder if that’s inaccurate or type theory doesn’t cover what imperative/OOP programmers normally call types. Or do they have another definition of structure outside the mainstream definition. I have a feeling a certain amount of confusion that happens in type theory discussions might have started right there.

                Regarding the RedPRL prover, one person on it is the person who wrote Practical Foundations for Programming Languages. People might find those interesting by themselves.

                1. 8

                  An example of something that structural interpretations of types have trouble with is equality of functions. Many programming languages have a very limited or outright broken notion of function equality, but it’s important if you want to prove a program conforms to a specification. It’s also important to have a language for what function equality means. Are a quicksort and a merge sort equal functions?

                  Bob Harper is both the author of this blog post and PFPL.

                  1. 3

                    Type theory is a theory of computation that classifies programs according to their behavior, rather than their structure. Types are themselves programs whose values stand for specifications of program equivalence.

                    I took “behaviour” to mean the ‘externally visible’ aspects of a value/program. For example, that something with function type Foo -> Bar can be called with a Foo value to produce a Bar value; that something with product type Foo * Bar can be projected to give both a Foo value and a Bar value; that something with sum type Foo + Bar can be projected to give either a Foo value or a Bar value but not both; etc.

                    I took “structure” to mean the ‘internal’ aspects of a value/program. For example, the different parts it may be made out of, any functions/computations that it uses (delayed or already completed), the syntax it was written as, etc.

                    In this sense:

                    • We can make these “behaviours” as complex and precise as we like, by making the types more complex and precise. For example, with dependent pairs and functions we can encode arbitrarily complicated mathematical relationships between values, inputs/outputs, etc.

                    • We can’t use types to classify the “structure” of a value/program.

                    As a provocative example, we can think about a value of product type Foo * Bar. We might think that the type tells us some ‘structural’ properties about the value, like “it contains a Foo” and “it contains a Bar”, but that’s not quite right. All we know is the “behaviour”, that we can project the value to get a Foo and a Bar, but we don’t know what will happen during that process. For all we know, the value might be an integer plumbed into an ‘unpairing’ function, like with Cantor pairing or Goedel numbering. Should we say that such a value “contains” a Foo and a Bar? What if that same integer is also used elsewhere to generate a boolean, a list of bitmaps, etc.? We’d at least have to weaken what we think of as “containing” or “structure”.

                    One example which I’ve heard discussed about type theory as opposed to set theory, is that in set theory we can (and very often do) ask whether a value is contained in a set, e.g. foo ∈ {foo, bar} and foo ∈ {high, low} are propositions which may be true or false. In type theory we can’t ask whether a value has a particular type, e.g. foo : (Foo * Bar) and foo : (High * Low) aren’t true/false propositions; they’re logical statements, which may be well-typed (valid) or ill-typed (invalid).

                    This is important for the “behaviour” vs “structure” distinction, since in set theory we can distinguish between isomorphic sets, e.g. {foo, bar} and {high, low} are isomorphic (they “behave the same”), but the foo ∈ x predicate is true for one and false for the other (they’re structured differently). Since type theory doesn’t let us do this, isomorphic types are indistinguishable (within the theory), so we can only reason about “behaviour”. This has connotations for modularity, reusability, optimisations, etc.

                    1. 3

                      You’re right that the types discussed in here are not being used in the same way that types in many c likes primarily use them. The goal is essentially different in scope. C types mostly capture the size of memory required for allocation of something. Types in the type theory sense can capture this idea, but can also contain much richer and complicated concepts in the types themselves.

                      also aside from PFPL Bob Harper is known for his work on SML and LF

                      1. 2

                        Yeah I don’t think you can do type theoretic proofs in say C# without a lot of work with interfaces and tuples. After all you don’t even have “Product and Sum types”. Heck it’s even hard right now in F# due to the fact that you have to explicitly name everything.

                        So for example say you have a function which takes a tuple of an int and (bool or string) and returns either a tuple of an int and a bool or and int an a string. int * (bool | string) -> (int * bool) | (int * string) //this would be the function signature. More amusingly

                        Old * (dumb | fat) -> (Old * dumb) | (Old | fat) // if I’m old and (dumb or fat) this implies I’m either old and dumb or old and fat

                        This is equivalent to A ^ (B v C) = (A ^ B) v (A ^ C). in logical propositions.

                        we can the write a function which takes an int and either a bool or a string, and return an int and bool, or an int and string and we know we have covered all possible return combinations.

                        In this way the function signature, the “Type” represents the lemma and then function itself exists as proof.

                        This as I understand it is merely the beginning of the whole thing. There’s a good computerphile video on the subject.

                      1. 4

                        Your best bet is probably to avoid modifying the environment at all, if possible.

                        This is sound advice. Consider the environment immutable and your sanity will be preserved.

                        1. 3

                          This starts getting a bit rough when you’re using libraries that are relying on stuff like locale.

                          I don’t know if the solution is to edit the environment, but the fact that so many C libraries change behavior based on env bubbles up in so many places. Bit of a rough foundation

                          1. 2

                            I treat the environment as immutable, but often call sub-processes with altered/augmented environments, e.g.

                            #!/usr/bin/env bash
                            doSomethingWith "$FOO" "$BAR"
                            FOO="A different foo" someOtherThing
                            

                            For LOCALE in particular, HaskeIl (compiled/interpreted with GHC, at least) will crash on non-ascii data if LOCALE isn’t set to an appropriate value, so I often invoke Haskell programs with LOCALE="en_US.UTF-8" myHaskellProgram.

                            I run into this a lot in build scripts (e.g. using Nix), since their environments are given explicitly, to aid reproducibility (rather than, say, inheriting such things from some ambient user or system config).

                            I imagine this would be extra painful if using libraries which need conflicting env vars.

                            Racket handles this quite nicely using dynamic binding (AKA “parameters”), which we can override for the evaluation of particular expressions. It feels very much like providing an overridden env to a sub-process. For example, I wrote this as part of a recent project (my first Racket project, actually):

                            ;; Run BODY with VARS present in the environment variables
                            (define (parameterize-env vars body)
                              (let* ([old-env (environment-variables-copy (current-environment-variables))]
                                     [new-env (foldl (lambda (nv env)
                                                       (environment-variables-set! env (first  nv)
                                                                                   (second nv))
                                                       env)
                                                     old-env
                                                     vars)])
                                (parameterize ([current-environment-variables new-env])
                                  (body))))
                            

                            Looking at it now, it might have been nicer as a macro, like (with-env (("FOO" "new foo value")) body1 body2 ...).

                            1. 2

                              Locale- defined behaviour is indeed an ugly little duckling that will never become a swan. One of the weird bugs I’ve been called into over the years was a C+Lua VM + script that was outputting CSV with “,” as element separator in a longer processing chain (system was quite big, simplified for the sake of story).

                              The dev had been observant and actually checked the radix point and locale before relying on its behaviour in printf- related functions. Someone had linked in another library though, that indirectly called some X+Dbus nonsense, received language settings that way, and changed the locale mid-stream - asynchronously. Sometimes the workload was finished, sometimes a few gigabytes had gone by and corruption was a fact as floats turned into a,b rather than a.b after a while…

                          1. 1

                            Hmm, isn’t node.js based on the JS interpreter from Chromium? It would be rather amusing if Firefox needs such components to build. Like GCC requiring LLVM.

                            1. 16

                              If folks actually read this story Firefox is working pretty hard to make this a non invasive, non privacy compromising feature change, and they’re also opening themselves up for public comment.

                              Consider voicing your objections rather than simply jumping ship. Having a viable open source option is important for the web ecosystem IMO.

                              1. 15

                                If folks actually read this story Firefox is working pretty hard to make this a non invasive, non privacy compromising feature change, and they’re also opening themselves up for public comment.

                                i just want a freaking browser engine with the possibility of enhancements via extensions. i don’t want to turn off magick features. i just want a browser which displays websites. the new firefox engine is really great, but i fear that now they slowly fill firefox with crappy features until its slow again.

                                1. 3

                                  What happens on the “New Tab page has zero effect on page load times. If you don’t like what the New Tab page looks like, customize it. Some of your options are:

                                  • set it to blank
                                  • install an extension that takes over
                                  • customize the current experience

                                  For the last option, click the little gear icon in the top right and you should see this https://imgur.com/a/1p47a where you can turn off any section that you don’t like.

                                  1. 7

                                    yes, i know. i still don’t want these features shipped and active by default. if i want pocket, i could install it as extension. heck, i wouldn’t mind if they said “hey, we have this great extension named pocket, care to try it out?” on their default new page, with a link to install it. but not shipped by default.

                                    1. 4

                                      What happens on the “New Tab page has zero effect on page load times.

                                      I don’t care so much about page load times; sites which care are already fast (e.g. no JS BS), whilst those which don’t will soon bloat up to offset any increase in browser performance.

                                      My main complaints with Pocket/Hello/Looking Glass/pdf.js/etc. are code bloat, install size, added complexity, attack surface, etc.

                                      1. 1

                                        You can’t do that on mobile.

                                  1. 4

                                    I upgraded from a “dumb phone” to an OpenMoko FreeRunner 10 years ago, and haven’t used any other phone since. It does calls and SMS, and I can browse the Web over WiFi if needed (it might support mobile data, but AFAIK my SIM only supports WAP). I often carry it around, but mostly don’t bother. It’s usually turned off, since I don’t like being tracked. It’s useful if someone wants to pre-arrange a call, or I know I’ll need to coordinate with someone.

                                    People keep trying to give me their “old” phones, but I don’t accept them as they’re downgrades in terms of privacy and freedom. I’m interested to see how the Purism phone works out, since I think that will have better isolation between the motherboard and the modem (the one component of the FreeRunner which has a binary blob driver). The hardware kill switches seem like a nice feature too (I hope the speaker has one, since malware can co-opt them into being microphones).

                                    I’ve never used Facebook, MySpace, Instagram, etc. so couldn’t comment on them. I did use Twitter for a while, since their terms used to be reasonable, and I could use an XMPP bridge rather than visiting their Web site. I quit after they met with the UK government in 2011 to discuss whether to shut down in ‘times of crisis’. It looks like they changed their terms of use soon after, banning a bunch of the third party apps which made it useful.

                                    1. 5

                                      I was TA for a course which involved Haskell programming, and we made heavy use of QuickCheck (similar to Hypothesis, but for Haskell rather than Python) for testing each student’s code. A couple of things to take into account:

                                      • If you’re going down this route, try to write your questions and tests at the same time. If you hand out the questions, give the students a week to write solutions, then try to come up with some tests to help you grade them, you’ll probably notice some annoyances which make testing harder (e.g. needing custom data generators).

                                      • Tests are great for checking if code does/doesn’t do the job, but that’s of releatively low importance in education.

                                      Regarding this second point, the main benefit we got from using automated tests in this way was that they freed up a lot of the (very limited) one-on-one time we had with each student. Rather than wasting time figuring out if the code works, we would run the tests to ‘get that out of the way’ in a few seconds, then have an in-depth discussion about some aspects of the code.

                                      If a test failed, we could talk about that: “why do you think it gave this output?”, “did you expect that part to work?”, “talk me through what you think this piece of code is doing”, etc. If all of the tests pass, we could pick some aspect of the code and ask about it.

                                      Many students seemed annoyed that they didn’t just pass/fail based on whether their code worked, but the discussions were more important. Some memorable moments:

                                      • One student’s “explanation” of their code was “that’s what someone put on Facebook”. I didn’t care if they got it from elsewhere, but since they couldn’t even begin to explain it I couldn’t give them any marks.
                                      • Another had used function composition in part of their code (written like foo . bar in Haskell), which we hadn’t covered. I spotted this and asked them what the dot does, and they didn’t know (they said they took it from Stack Overflow).
                                      • A few students wrote things like if foo > bar == True then ... and insisted it was “right” because it gave the correct answer. In these cases I didn’t give full marks since IMHO it indicated a lack of understanding, since == True is a useless no-op. If failing code can get partial credit for doing some things right, then passing code can get partial credit for doing some things wrong.
                                      1. 4

                                        Same here. We took it a step further though. We organized the faults we found in student code via property based testing into equivalence classes. This helped us give students feedback and sometimes informed grades.

                                        Here is the rubric we used (this does not contain weights, which varied): https://www.cs.tufts.edu/comp/105-2017s/coding-rubric.html — we probably wrote a couple hundred words of feedback to each student for each assignment. It was amazing.

                                        1. 3

                                          One thing a few CS classes did when I was an undergrad (admittedly at a kind of unusual university) was to let students submit to the autograder ahead of time. It was fully unattended and would email back in a few minutes with either “passed all tests” or something like “when we feed your program X, it outputs Y, which isn’t right”. You still didn’t necessarily get a perfect grade if you passed all the autograder tests, but it avoided students handing in code broken in trivial ways, at least if they were diligent enough to run the tests and fix the obvious bugs first (and if they weren’t, they could hardly complain about the grade).

                                          This was early 2000s, so the autograder was just a battery of hand-written tests accumulated over the years to catch the most common mistakes, not anything as fancy as QuickCheck, but it was pretty effective I think at, like you say, avoiding the feedback being entirely about trivial correctness issues.

                                          1. 2

                                            submit to the autograder ahead of time. It was fully unattended and would email back in a few minutes with either “passed all tests” or something like “when we feed your program X, it outputs Y, which isn’t right”.

                                            I think that would be nice to have for beginners in any language accompanied by some standard text and examples. It would definitely save time tracking down their early mistakes. They can try to figure it out themselves but don’t even really know the basics at that point. Better to help them there with their own debugging happening on stuff they should understand by that point.

                                        1. 3

                                          Typewriters were optimized for, well, typing.

                                          Within significant technological constraints of metallurgy, plastics, and mechanics that now offer vastly different tradeoffs. Even the language is different: when’s the last time you saw a semicolon outside of code?

                                          1. 17

                                            One of my goals in life is to be able to properly use a semicolon in the normal course of my writing; it’s not as hard as you would think.

                                            1. 4

                                              I often use them in SMS; I see it as a kind of challenge.

                                              1. 1

                                                A colon or plain period would be more appropriate than semicolon here.

                                            2. 3

                                              I use them all the time; much better than comma splices.

                                              1. 3

                                                Ironically this would not have been a comma splice, and a comma would be more appropriate.

                                                1. 1

                                                  What is your criteria for a comma over a semicolon? It looks good to me.

                                                  1. 8

                                                    The clauses aren’t independent. Actually that form is specifically used for dependent clauses. For example, “I use them all the time, more than before.” The base “I use them” applies to both parts: “I use them all the time” and “I use them more than before.” But “I use them much better than comma splices” doesn’t make any sense, so that’s not what’s happening here. Forty-Bot is omitting “they are”—typically handled with an emdash, or parentheses if the additional content has only minor significance.

                                                    For a semicolon to apply, “they are” must be included to create a second independent clause:

                                                    I use them all the time; they’re much better than comma splices.

                                                    Using a comma instead of an emdash is mildly incorrect, but widely accepted in conversational writing. Since Forty-Bot explicitly called out the comma, I only pointed out the comma would be more appropriate. Though an emdash would be most appropriate. Semicolons see little use because conversational writing favors such omissions.

                                                    1. 2

                                                      Thank you for the reply, this is very informative.

                                                      1. 1

                                                        Semicolons are also useful for: separating list elements, when they contain commas; showing off, often in language discussions :)

                                              1. 2

                                                It’s fun to see this sort of thing getting on the front page of lobste.rs, hackernews, etc. since it’s inevitably going to be:

                                                • Used by a lot of people, e.g. after searching for how to detect chrome headless.
                                                • Be made useless within a matter of days, as chrome devs or users systematically remove these differences.

                                                When I was an undergraduate, a bunch of Sun thin clients got installed on campus, locked down to only run Firefox, and only show the University Web “portal” (webmail, etc.).

                                                It was fun to find ways around these restrictions. It was easy enough to visit arbitrary Web pages, by putting the URL in a draft email. Having a draft containing google.com would allow access to anywhere. More interesting was to add an empty attachment with a ‘.tar.gz’ filename suffix, which would let you bring up Firefox’s “open with” dialogue. Open with /usr/bin/xterm or /usr/bin/gnome-session to get access to a nice unix terminal/desktop to work from (all the other campus machines were windows)

                                                Whenever we found a working method, we’d post it to a thread on our student society forum. Sure enough, someone from IT would read the thread and block off those mechanisms, so we’d find some more and post those :)

                                                1. 1

                                                  Back when I was a temp bank cashier (late 90s) I amused myself by connecting to a new system (based on AIX as opposed the mainframe-based interfaces) and was able to get a (user) shell by invoking help and ctrl-Z’ing.

                                                  Being a good boy (and mindful of all my activity being tracked) I sent an email to the head office detailing my “exploit”. I don’t know if they took action though.

                                                1. 3

                                                  If it’s too much work, don’t do it? Left unanswered is why it’s necessary to post links to your blog on every site imaginable.

                                                  1. 1

                                                    Yes, my feeling when reading this was that in the “good old days” we had a heterogeneous, distributed, self-organising network of people doing and making things they like, others discovering, reading and sharing it, and communities and services forming to facilitate this.

                                                    Then Facebook, Twitter, etc. gobbled up all of this into their giant silos, all in the name of ‘user engagement’, ‘content consumption’, etc. and this is bad.

                                                    Yet, the author then goes on about which of these silos give their blog posts the most ‘engagement’, or how to format posts for the highest ‘consumption’, etc. which sounds exactly like the ‘attention hijacking’ BS which lead to those silos.

                                                  1. 4

                                                    The author gives this as a bad example of 100% coverage (since it doesn’t check division by zero):

                                                    public void divide_with_valid_arguments() {
                                                      assertThat(new Calculator().divide(10, 2)).isEqualTo(5);
                                                    }
                                                    

                                                    Yet it’s even worse than that, since this also gives 100% coverage:

                                                    public void divide_with_valid_arguments() {
                                                      new Calculator().divide(10, 2);
                                                    }
                                                    

                                                    A test suite with 100% coverage doesn’t actually need to assert anything! It does check that there’s a way to run each branch without hitting a language error (undeclared variable, segfault, type/tag error, whatever), but many compiled languages will catch these things anyway, making code coverage even less useful there.

                                                    1. 3

                                                      Yeah, this is an issue. What would be nice is a way for the test framework to say “some kind of test or assertion was (not) executed against this line/branch of code”, rather than “this code was executed at some point while tests were running” and then ideally you could see exactly what those assertions were. I’m not aware of a way to do that in any languages I know (Python and JS). It’s easy to forget that executed doesn’t necessarily mean it was tested.

                                                      1. 3

                                                        It does check that there’s a way to run each branch without hitting a language error (undeclared variable, segfault, type/tag error, whatever), but many compiled languages will catch these things anyway, making code coverage even less useful there.

                                                        Not necessarily! If you use code contracts, then it’s still worthwhile to write tests without assertions. That’s because you’re putting the assertions in the code itself, so all the test needs to do is explore the state space. Example:

                                                        @require("`x` must be in `l`", lambda a: a.x in a.l)
                                                        @ensure("`x` must be removed from `l`", lambda a, r: a.x not in r)
                                                        def remove(l, x):
                                                          l.remove(x)
                                                          return l
                                                        
                                                        @given(lists(integers()), integers())
                                                        def test(l, x):
                                                          assume(x in l)
                                                          remove(l, x)
                                                        

                                                        Even though there’s no assertions in the test, it will still fail, because remove([0, 0], 0) violates a postcondition.

                                                      1. 3

                                                        Thanks for this submission. My hunch is that an architecture which makes e.g. caching and speculative execution an observable part of the API is the better approach. Afaiu mips does something similar and compilers learned to deal with it.

                                                        1. 1

                                                          My own hunch is that we should be avoiding impure operations like getting the current time.

                                                          This post seems to be talking about trusting high-assurance languages for critical/sensitive tasks, and how those guarantees can be undermined if we run arbitrary machine code. That problem seems too difficult to me: surely a better langsec approach would be for the arbitrary code to be in a high-assurance language, with the only machine code we execute coming from trusted critical/sensitive programs?

                                                          I would think a langsec approach to e.g. preventing timing attacks in Javascript is to make Javascript (the language) incapable of timing. Or, at least, providing a logical clock (also used for the interleaving of concurrent event handlers) rather than allowing access to the actual time.

                                                          1. 2

                                                            For the vast majority of uses of a computer at some point the application will need to know what time it is. Avoiding impure operations is throwing up your hands on general computing as a useful tool. I don’t think this is quite what you meant to say though. Can you clarify?

                                                            1. 2

                                                              The clock is a sensor and needs to be treated as such with permissions and similar. Many applications don’t have a need for the clock.

                                                          1. 2

                                                            I missed the (2008) in the title and assumed this was posted recently. It’s disappointing how many of these problems are still relevant in Haskell today.

                                                            sometimes the type classes which are there are poorly thought out (Num shouldn’t extend Eq, and it would be nice if + was factored out in order to provide better support for things like vector spaces).

                                                            I wouldn’t mind different typeclasses for every operation (Eq, Addable, Subtractable, etc.) with common cases such as Num around as a quick way to define many instances concisely. But this is Haskell, which means that smarter people have probably thought of this and decided against it…

                                                            1. 0

                                                              But this is Haskell, which means that smarter people have probably thought of this and decided against it…

                                                              AFAIK the smart people decided against changing the default, since it would be a large breakage for mostly subjective reasons (similar changes, like making Applicative a superclass of Monad, have more objective, definitive arguments in their favour (e.g. we can derive an Applicative from a Monad, so it’s “already there” in a sense)).

                                                              Meanwhile, lots of smart people have made alternative preludes which are opt-in for those who don’t want the default.

                                                            1. 3

                                                              A few off the top of my head (I mostly have experience with Make, Nix and Cabal):

                                                              • Tools which assume they’re interacting with a user, making scripting harder. This is actually an umbrella problem for some of the others below.
                                                              • Special snowflake languages (config or script). The problem isn’t whether or not it’s hard for a person to learn the syntax; it’s that any new language is automatically incompatible with all existing tooling (parsers, pretty-printers, formatters, editor shortcuts, etc.). Just use JSON/XML/s-expressions unless there’s a very compelling reason not to (and “too many parens” isn’t a compelling reason; see e.g. sweet expressions, etc.). Guix seems to be the least offensive in this regard ;)
                                                              • Closed ecosystems. For example, Cabal (by default) looks up package names from Hackage. If I want to use e.g. a git repo, I need to add it using command invocation.
                                                              • Language-specific assumptions. If a Haskell package depends on other Haskell packages, Cabal can build it. If it depends on some non-Haskell package, then it can’t. Except for special snowflakes like zlib.
                                                              • Hard-coded special cases. For example, looking up some particular tools (e.g. happy) but not allowing users to specify their own tools to be looked up.
                                                              • Implicit changes in behaviour due to the environment, e.g. whether or not certain directories exist, or their contents, when those directories aren’t explicitly defined on the commandline or some env var. Cabal was notorious for this, prior to the “sandbox” feature.
                                                              1. 1

                                                                Closed ecosystems. For example, Cabal (by default) looks up package names from Hackage. If I want to use e.g. a git repo, I need to add it using command invocation.

                                                                Cabal is fixing this

                                                                Hard-coded special cases. For example, looking up some particular tools (e.g. happy) but not allowing users to specify their own tools to be looked up.

                                                                This too I think, with build-tool-depends

                                                              1. 2

                                                                I’m a big fan of property checking, but find it a little painful in dynamically typed languages, since we need to pass around generators explicitly. One reason Haskell’s QuickCheck is really nice to use is that a few rather simple things end up complementing each other:

                                                                • A “default” random generator can be defined for any type (e.g. instance Arbitrary Customer where ...)
                                                                • Default generators can be defined for composite datatypes, e.g. a generator for List t which automatically uses the generator for t (e.g. instance (Arbitrary t) => Arbitrary (NonEmptyList t) where ...)
                                                                • Type inference can figure out what type a value has to have, and the type class mechanism will figure out the default generator to use for that type

                                                                This means our properties are often as simple as e.g. printParseInverse x = parse (print x) == x

                                                                The other annoyance of property based testing is crafting generators to satisfy some precondition. This isn’t so difficult in Haskell due to the culture/best-practice of trying to write total functions: if a function needs the input data to have some particular structure, that should be encoded in the type. On the other hand, if the input type doesn’t encode some precondition (e.g. because it would be too tricky to represent) then, culturally, we’d usually say that values violating the precondition are still valid, since the function’s type claims to accept them, and hence it’s up to the function to handle them in some way (e.g. with a Maybe result).

                                                                Hence with QuickCheck we can normally make do with the default generators, and have Haskell do all of the plumbing to make that work. The only reason to avoid default generators are those times when they give poor coverage of the cases we care about.

                                                                1. 3

                                                                  On the other hand, if the input type doesn’t encode some precondition (e.g. because it would be too tricky to represent) then, culturally, we’d usually say that values violating the precondition are still valid, since the function’s type claims to accept them, and hence it’s up to the function to handle them in some way (e.g. with a Maybe result).

                                                                  I think there’s some cases where what you want isn’t easily specifiable in many type systems, for example “two numbers that are not equal”. If it’s on a helper function, the main function should have already taken care of the invalid case, such as by returning an error instead of going through with the computation. In that case, violating the helper’s precondition is definitely a bug in the main function, and we should error.

                                                                  1. 2

                                                                    I think there’s some cases where what you want isn’t easily specifiable in many type systems, for example “two numbers that are not equal”.

                                                                    Of course. The reason I mentioned “culture” is about how we handle violations, and where we place the blame. In my experience, the “Haskell culture” would be the following:

                                                                    If it’s a function which can be imported from elsewhere, then it should never “go wrong”. The function is responsible for checking that the precondition is satisfied, and if it doesn’t check then it’s a bug. The easiest way to handle blame is to have a Maybe return type, and return the value Nothing if the check failed. Since the caller gave us dodgy arguments, they have to deal with the Nothing case. Callers might use a partial function like fromJust to extract the answer if they’re confident that the arguments they give will always work.

                                                                    If the function isn’t part of the public API, but it is widely used inside a library/application, then it should usually follow the rules above.

                                                                    If the function has a very narrow scope, e.g. it’s a helper defined in a let or where block, then it’s fine to be partial and assume valid arguments will be given. Any problems are the fault of the enclosing function (of which the partial function is just one part).

                                                                    For example, we might have the following function which requires non-equal arguments:

                                                                    dodgyCalc :: Int -> Int -> Rational
                                                                    dodgyCalc x y = 2 % (x - y)
                                                                    

                                                                    We could expose this with a Maybe:

                                                                    safeCalc :: Int -> Int -> -> Maybe Rational
                                                                    safeCalc x y = if x == y then Nothing else Just (2 % (x - y))
                                                                    

                                                                    Or we might give it a narrow scope:

                                                                    outerFunc :: Int -> Rational
                                                                    outerFunc n = dodgyCalc n (n + 1)
                                                                      where dodgyCalc x y = 2 % (x - y)
                                                                    

                                                                    There are of course exceptions, like when checks are expensive. Even in these situations it might make sense to come up with a “safer” API over the top, e.g. providing a bunch of callbacks and having them plumbed together in a way which either guarantees the precondition, or only checks it once instead of repeatedly.

                                                                1. 2

                                                                  Whenever I come across a PDF I always save a copy before opening it, since they’re very cheap to store and often useful to refer to. I try to add them to a large Bibtex file too, but that takes more effort so I’ve only gotten through a fraction of them.

                                                                  I have a directory of “talks” for lectures, presentations, etc. which I’ve found interesting enough to keep (mostly saved from YouTube). I only move stuff in there after watching, and deciding whether or not I might ever watch it again. I have a subdirectory for TED talks, since (a) I’ve saved loads and (b) their short/shallow nature is rather different than the long/deep nature of a lecture.

                                                                  I don’t trust sites like Github with hosting code; they’re useful as mirrors, but I keep clones of all the software I’ve ever written, along with all the projects I’ve ever bothered checking out or cloning (outside of build scripts).

                                                                  1. 1

                                                                    Yeah, I also save most of my PDFs. I don’t save them mostly by accident, as I can skim it quickly in browser and forget to hit the download button. But I don’t organize them immediately after downloading, sadly, so I tend to have a lot of them to the point it becomes a pain and I start organizing them, but usually fail to do so to the full extent.

                                                                    Some conferences used to provide content on their FTP servers, or HTTP servers with file index, and in such cases lftp is invaluable, because it can non only work with FTP, obviously, but even with file index on HTTP, which is not a widely known feature. So yeah, you can do a mirror of file server served over HTTP using it. I find wget with its recursive features a bit more clunky here. With lftp I can for instance estimate needed free space by invoking du -hs command, and it will visit each subdirectory to do the calculation. Sometimes some non-standard file index are used, so it may not always work, but it’s still a great feature.

                                                                    GitHub is not better or worse than most other code hosting solutions (if we’re talking about sole repository hosting that you access using your favorite tools remotely). But as long as a decentralized VCS is used under the hood, you can easily fully clone it (git clone --mirror and similar solutions), which is a great thing. But I very rarely mirror repositories and main branch is usually good enough.

                                                                  1. 7

                                                                    Some Haskell features are just mistakes; others are designed for PL researchers to experiment, but their usefulness in industrial programming is yet to be proven.

                                                                    I’d like to see a list of which Haskell features aren’t considered good for production code, and why! I’ve decided TransformListComp is one, because it has zero uptake in the community. Years ago I saw ImplicitParams go horribly wrong, but haven’t tried it since.

                                                                    On the other side, I love OverloadedStrings and GADTSyntax. I asked on twitter, and the list of batteries included extensions included OverloadedStrings, ScopedTypeVariables, LambdaCase, InstanceSigs, and TypeApplications.

                                                                    Got any features you feel are good or bad for production Haskell?

                                                                    1. 2

                                                                      This is a good start on an answer: https://stackoverflow.com/a/10849782/108359

                                                                      1. 0

                                                                        Lazy evaluation makes it hard to reason about the space usage or even termination of a program. I prefer having eager evaluation with lazy data structures.

                                                                        1. 8

                                                                          The downside is that you can’t use functions for control flow. An obvious example is Church encoding, e.g.

                                                                          true x y = x
                                                                          false x y = y
                                                                          ifThenElse c x y = c x y
                                                                          

                                                                          If we call ifThenElse eagerly, both branches x and y will be calculated; if we used these to e.g. select between a base case and a recursive case, we’d cause an infinite loop.

                                                                          Most eager languages resort to a built-in lazy if construct (Haskell has one too, but it’s unnecessary), which forces us to reframe our problem in terms of some built-in boolean type rather than custom domain-specific types (for a similar issue, see boolean blindness).

                                                                          On the one hand, (Church) booleans are quite a weak argument for laziness since “real programs” will be using some richer, more meaningful, custom datatypes instead; yet on the other hand, most eager programs end up relying on booleans, since they’re the only thing that works with the built-in if!

                                                                          Whilst an eager language with things like case and pattern-matching can alleviate some of these problems, I still think that lazy function calls are important. In fact, I think this is a blub paradox, since even eagerly evaluated languages provide lazy && and || functions; but again, this is usually limited to hard-coded, built-in constructs.

                                                                          1. 4

                                                                            The lazy if example always stroke me as silly. It is actually less elegant than you might think: to call it, you need to create two thunks, even though you know in advance that (at least) one will be discarded. I know that optimizing compilers can get rid of the overhead, but that would be an implementation detail that isn’t immediately justified by the semantics of the language in question. In any case, you don’t need if as a built-in in a strict language. It is nothing but syntactic sugar for pattern matching on Bool. Similarly, arithmetic if is syntactic sugar for pattern matching on an Ord produced by a call to compare. Etc. etc. etc.

                                                                            By making a sharp distinction between values and computations, strict languages gain expressivity over lazy ones. If you want to recover the benefits of laziness in a predominantly strict language, you can always define an abstract type constructor of thunked computations - that can be implemented in 20-30 lines of ML. On the other hand, adding seq to a lazy language wreaks havoc in its semantics, weakening or destroying many free theorems, presumably the main reason for preferring a lazy language over a strict one.

                                                                            EDIT: Wording. Broken link.

                                                                            1. 3

                                                                              In any case, you don’t need if as a built-in in a strict language. It is nothing but syntactic sugar for pattern matching on Bool. Similarly, arithmetic if is syntactic sugar for pattern matching on an Ord produced by a call to compare. Etc. etc. etc.

                                                                              Yes, I hand-waved this away as “things like case and pattern-matching” :)

                                                                              a sharp distinction between values and computations

                                                                              From this perspective, my point could be phrased as laziness “unifying” values and computations. In the if example, we can implement it eagerly by making the thunks explicit, e.g. giving true and false type (Unit -> a) -> (Unit -> a) -> a. Or should that be (Unit -> a) -> (Unit -> a) -> Unit -> a? Maybe we need both…

                                                                              One of my favourite things about programming is when I come across unifications of concepts I’d previously considered separate. Examples which come to mind are Python classes being first-class objects, Smalltalk control-flow statements (e.g. ifTrue:) being method calls, Lisp code being data (in a way that’s both useful (unlike strings) and looks the same unlike complicated AST encodings), and pointers being ints in C.

                                                                              Whilst I accept that we may lose expressivity, and this can certainly be important for resource-constrained or -intensive tasks, for the sorts of inefficient plumbing I tend to write I very much like the symmetry that is gained from having fewer distinct concepts/constructs/etc. to take into account (especially when meta-programming!).

                                                                              adding seq to a lazy language wreaks havoc in its semantics, weakening or destroying many free theorems

                                                                              That’s certainly a problem, I agree :(

                                                                              1. 4

                                                                                Yes, I hand-waved this away as “things like case and pattern-matching” :)

                                                                                Handwaving is failing to provide a proof. What you did was simply make a wrong statement:

                                                                                Whilst an eager language with things like case and pattern-matching can alleviate some of these problems, I still think that lazy function calls are important.

                                                                                Pattern matching isn’t there to “alleviate” any “problems” caused by the lack of laziness. Pattern matching is the eliminator for sum types, which, by the way, don’t actually exist in lazy languages.

                                                                                since even eagerly evaluated languages provide lazy && and || functions; but again, this is usually limited to hard-coded, built-in constructs.

                                                                                In a strict language, && and || are not functions, but rather syntactic sugar over nested uses of pattern matching on the boolean type.


                                                                                From this perspective, my point could be phrased as laziness “unifying” values and computations.

                                                                                You aren’t unifying anything. What you’re doing is ditching values altogether, and replacing them with trivial computations that return those values. In your limited world, values only exist “at the end of the computation”, but they are not first-class mathematical objects that you can, say, bind to variables. So you are deprived of any proof techniques that rely on variables standing for values, such as structural induction.

                                                                                On the other hand, if you properly place value types and computation types in separate categories, you will find that these categories are related by an adjunction and admit different universal constructions. Explicitly acknowledging this structure allows you to define abstractions that don’t break in the face of nontermination or explicit thunk-forcing, unlike most of Haskell’s library ecosystem.

                                                                                One of my favourite things about programming is when I come across unifications of concepts I’d previously considered separate.

                                                                                If essential properties of the objects being “unified” are lost, it stops being “unification” and becomes “conflation”.

                                                                                for the sorts of inefficient plumbing I tend to write I very much like the symmetry that is gained from having fewer distinct concepts/constructs/etc. to take into account (especially when meta-programming!).

                                                                                If anything, you have destroyed the symmetry (duality) between values and computations.

                                                                                EDIT: Added remark about conflation. Fixed typo.

                                                                                1. 3

                                                                                  Pattern matching isn’t there to “alleviate” any “problems” caused by the lack of laziness. Pattern matching is the eliminator for sum types

                                                                                  Elimination is when we select one of the branches. That can be done before or after evaluating them. That’s the distinction I was making: in my made-up terminology an “eager case expression” would evaluate all branches to normal form, then select the appropriate one to return; a “lazy case expression” would select the appropriate branch before attempting to evaluate any of the branches. You’re right that if, && and || in eager languages are syntactic sugar over pattern matching (case), but my point was that even eager languages use the “lazy case expression” variety, not the “eager case expression”.

                                                                                  The way those languages achieve such laziness is by making pattern-matching a special language construct. We can’t write user functions which behave the same way (unless we distinguish between values and thunks); note that we can write macros to do this, as is common in Lisps.

                                                                                  With lazy function calls, such “lazy case expressions” can, in principle, be replaced by eliminator functions (like Church encodings and their variants); although I currently think that would be less nice (see Morte and Dhall, for example).

                                                                                  If essential properties of the objects being “unified” are lost, it stops being “unification” and becomes “conflation”.

                                                                                  Sure, but what counts as “essential” is subjective. C programmers might consider pointer arithmetic to be essential, which is lost by high-level representations like algebraic (co-)datatypes. Personally, I’m perfectly happy to e.g. conflate scope with lifetime (i.e. garbage collectors based on liveness).

                                                                                  I don’t have particularly strong opinions when it comes to either distinguishing or conflating the distinctions used by CBPV, “polarity”, co/data, etc. That’s why I currently fall back on my “symmetry” heuristic.

                                                                                  If anything, you have destroyed the symmetry (duality) between values and computations.

                                                                                  By “symmetry” I didn’t mean “duality between two different sorts of thing”, I meant “treating all of these things in a single, uniform way” (i.e. conflating). Fewer distinctions means fewer choices to be made, fewer cases to be handled and fewer combinatorial explosions to tackle. This makes life easier when e.g. parsing, code-generating, interpreting, etc.

                                                                                  Of course, conflating is a simplistic thing to do; yet such ease is nice to have, so I treat it as the “activation energy” (the ease/value offered) that each distinction must overcome in order for me to use them (at the loss of symmetry).

                                                                                  Examples of distinctions which I think are generally worth it (for me):

                                                                                  • Type-level/value-level (as found in dependently-typed languages)
                                                                                  • Compile-time/run-time
                                                                                  • Pure/effectful

                                                                                  Distinctions I don’t think are worth it (for me):

                                                                                  • Type-level/kind-level
                                                                                  • Separate constructs for types/values (e.g. type-level computation in Haskell, type families, etc.)
                                                                                  • Statements/expressions
                                                                                  • Linear/non-linear types
                                                                                  • Concrete/abstract syntax (i.e. not using s-expressions)
                                                                                  • Value/delayed-computation (“polarity”)
                                                                                  • Partial/total
                                                                                  • Native/managed

                                                                                  Of course, these are just my preferences, and they vary depending on what problem I’m tackling and what mood I’m in ;)

                                                                                  1. 3

                                                                                    Elimination is when we select one of the branches. That can be done before or after evaluating them. That’s the distinction I was making: in my made-up terminology an “eager case expression” would evaluate all branches to normal form

                                                                                    This doesn’t make sense. Expressions under binders are not reduced in a call-by-value language, and the left-hand side of a branchn is very much a binder for any variables used as constructor arguments. So what you call “eager case expression” does not exist at all.

                                                                                    The way those languages achieve such laziness is by making pattern-matching a special language construct.

                                                                                    A strict language with pattern matching doesn’t “achieve laziness”. It simply evaluates arms at the correct moment.

                                                                                    Sure, but what counts as “essential” is subjective. C programmers might consider pointer arithmetic to be essential, which is lost by high-level representations like algebraic (co-)datatypes.

                                                                                    I’m not sure pointer arithmetic is essential, but array manipulation certainly is essential, and memory-safe languages do a very poor job of dealing with it. I’d be willing to give up memory-safety in exchange for a predicate transformer semantics for array manipulation, so long as the semantics is explicitly formalized.

                                                                                    Personally, I’m perfectly happy to e.g. conflate scope with lifetime (i.e. garbage collectors based on liveness).

                                                                                    I’m not. Languages without substructural types suck at manipulating anything that doesn’t last forever (e.g., file handles).

                                                                                    (i.e. conflating). Fewer distinctions means fewer choices to be made, fewer cases to be handled and fewer combinatorial explosions to tackle. This makes life easier when e.g. parsing, code-generating, interpreting, etc.

                                                                                    And it makes life harder when debugging. Not to mention when you want to formally prove your programs correct. (I do.)

                                                                                    1. 2

                                                                                      Expressions under binders are not reduced in a call-by-value language

                                                                                      Yes, that’s what I’m referring to as ‘a form of laziness’. Expressions under binders could be (partially) reduced, if we wanted to. Supercompilers do this (at compile time), as do the morte and dhall languages, for example.

                                                                                      what you call “eager case expression” does not exist at all

                                                                                      The reason that basically no language does this is because it’s a pretty terrible idea, not that it couldn’t be done in principle. It’s a terrible idea because it evaluates things which will be discarded, it introduces preventable non-termination (e.g. with base/recursive cases), and seems to have no upsides.

                                                                                      My point is that the same can be said for strict function calls, apart from the “no upsides” part (upsides of strict calls include preventing space leaks, timely release of resources, etc.).

                                                                                      array manipulation certainly is essential… I’d be willing to give up memory-safety… Languages without substructural types suck… makes life harder when debugging… when you want to formally prove your programs correct

                                                                                      I think this is where our priorities differ. I’m happy enough with an inefficient, simple(istic) language, where I can formally reason about “getting the right answer (value)”, but I don’t care so much about how we arrive there (e.g. order of evaluation, space usage, garbage collection, etc.)

                                                                                      1. 4

                                                                                        Expressions under binders could be (partially) reduced, if we wanted to. (…) The reason that basically no language does this is because it’s a pretty terrible idea, not that it couldn’t be done in principle.

                                                                                        Sure, but not in a call-by-value language. The distinguishing feature (a killer feature!) of call-by-value languages is that variables stand for values, which is not true if expressions are reduced under binders.

                                                                                        Supercompilers do this (at compile time), as do the morte and dhall languages, for example.

                                                                                        Except for simple cases where the program’s asymptotic time and space costs are not altered, this is a terrible idea. (Yes, even when the program’s performance is improved!) I want to reason about the performance of my program in terms of the language I am using, not whatever machine or intermediate code a language implementation could translate it into. The former is my responsibility, the latter is beyond my control.

                                                                          2. 5

                                                                            Lazy evaluation makes it hard to reason about the … termination of a program

                                                                            A program terminates at least as quickly when evaluated non-strictly as it terminates when evaluated strictly, so that can’t be true.

                                                                            1. 3

                                                                              For once I second @Yogthos. Lazy evaluation is certainly useful, but not as the default! If you care about algorithms, and IMO every programmer ought to, strict evaluation as the default makes analysis easier than lazy evaluation (“simply substitute and reduce” vs. “keep track of which thunks have been forced”) and gives tighter asymptotic bounds (worst-case vs. amortized) on running times.

                                                                              1. 4

                                                                                Strict evaluation should not be the default, you lose composition of algorithms.

                                                                                1. 2

                                                                                  Eta-expand, then compose. Voilà.

                                                                                  1. 1

                                                                                    Eta-expand then compose what?

                                                                                    1. 3

                                                                                      For instance, rather than say foo . bar, where foo and bar are arbitrarily complicated expressions, you say \x -> foo (bar x).

                                                                                      As for composing complicated algorithms - it’s totally overrated. Instead of designing libraries around a mess of higher-order functions, oops, Kleisli arrows, oops, profunctors, oops, who knows, I’d rather reify every intermediate state of a complex operation using a custom abstract data type, and then only export procedures that perform individual atomic steps - good old-fashioned first-order procedures! Composing these atomic steps into the exact algorithm the user wants is best done by the user himself. Maximum flexibility and no abstraction overhead. But of course, you need decent support for abstract data types for this to work.

                                                                                      EDIT: Added long paragraph.

                                                                                      1. 4

                                                                                        I also have to do that for everything in foo and bar, then manually fuse -> code duplication.

                                                                              2. 2

                                                                                Perhaps the issue is less direct: laziness tempts us to use infinite (co)datastructures, which we must handle carefully (e.g. don’t take the length of an infinite list).

                                                                                1. 1

                                                                                  Correction: I should have said “when evaluated lazily” not “non-strictly”. I think as written what I said was false.

                                                                                2. 2

                                                                                  By the way, that’s not really what Shapr meant by “which Haskell features aren’t considered good for production code”!

                                                                              1. 9

                                                                                Using nix to distribute RhodeCode was the best decision we made for how we distribute our software. It removed all problems we had with python packaging just using virtualenv. I think the key benefit is that our installation survives system upgrades while we had almost 40% failure rate in this case with previous virtualenv based installer.

                                                                                1. 5

                                                                                  Would you be interested in doing a whitepaper?

                                                                                  1. 3

                                                                                    Do you instruct end users to use Nix, or do you have some custom installer that somehow hides Nix from the users?

                                                                                    1. 5

                                                                                      We abstract it. End users just run a single binary that creates full nix env, and unpacks all dependencies into a custom nix store.

                                                                                      Nice things are that on upgrades we still have the full tree of old dependencies, so, for example, rollback to the previous release is just swapping a symlink.

                                                                                      1. 1

                                                                                        This reminds me of the guix pack command: https://www.gnu.org/software/guix/blog/2017/creating-bundles-with-guix-pack/

                                                                                        Would something like that (e.g. ported to Nix rather than Guix) make life easier? I imagine that including Nix itself in the artefacts you distribute would make things quite bloated. (I’ve never used Guix, but I’m a big Nix fan :) )

                                                                                  1. 1

                                                                                    I was an avid Debian user for many years, but I switched to NixOS before Debian adopted systemd. NixOS uses systemd quite heavily, and I’m not a fan of its scope creep.

                                                                                    The next time I need to install an OS I’m going to give GuixSD a try; being GNU, FOSS-only and Scheme-based are all major attractions to me; but so is the fact it doesn’t use systemd; it uses dmd/shepherd, which has been around for many years and AFAIK has similar advantages to systemd/uselessd/upstart/s6/etc. but without systemd’s feature creep.

                                                                                    1. 15

                                                                                      I agree with the title’s pun, but not its implied meaning. Whilst porn was the most obvious first application of this technology (the same goes for many technologies!), this looks like a decent way to erode the power of surveillance, especially the rampant spread of CCTV like here in the UK.

                                                                                      The article’s right that society will need to adjust to this new technology, but porn is just the tip of the iceberg. It won’t be long before we see fake videos of politicians and other high-profile figures in compromising situations; e.g. a clandestine lunch between Trump and Putin. At the extreme end, I’m sure the creators of “fake news” clickbait can’t wait to face-swap Hillary Clinton and Donald Trump on to e.g. ISIS beheading videos (either way around!). Imagine if those spreading the Obama “birther” conspiracy could convincingly face-swap Obama on to some footage of an African tribal ritual. The possibilities are endless.

                                                                                      As soon as these things start to surface, the powers-that-be will have no option than to spread the message that video footage can’t be trusted, at least without provenance/chain-of-custody information. With time, hopefully this will undermine the credibility of state and private monitoring infrastructure.

                                                                                      For example, most CCTV in the UK is privately operated. I can imagine operators splicing particular faces on to footage of criminal acts, for extortion/revenge/whatever. There’s also the possibility of faking/re-using crime footage, to defraud insurance firms; however, it’s probably still easier to physically act it out, rather than simulate the footage.

                                                                                      1. 7

                                                                                        Great comment and insight. On the flip side this also explodes the market for technology of certified video/photo. Checksums/signs/watermarks/etc can be added to original videos for cross reference and falsified videos debunked. For things like surveillance/CCTV there will need to be verifiable hardware/firmware for investigation when under scrutiny.

                                                                                        –EDIT– I can’t resist adding this related meme, from one of my fave DS9 episodes: http://knowyourmeme.com/memes/its-a-fake