Live data from Hacker News

Programming paradigms that change how you think about coding

brikis98.blogspot.com

101–110 of 206 posts

Re: Programming paradigms that change how you think about coding

#101
post #3

Some notes: - parallel and concurrent are 2 different things - the 'symbolic languages' definition seems off. Wikipedia puts it right: > symbolic programming is computer programming in which the program can manipulate formulas and program components as data So it's not "using graphs & such to program"

Take this[1] for example. That's a regular Mathematica notebook. You can insert images (like drag & drop them from your desktop) directly into notebooks. It's actually very convenient for image processing. The same thing holds true for things like the graphical output of plotting functions, which retain their underlying vector data.

The symbolic paradigm isn't necessary for this, but it makes it easier. Implementation-wise these things requires less "special case-ing" than you would think because the symbolic paradigm in essence encourages your representations to be naked. Thus images, plots, etc are not "black box" items. They're structured data represented in the same language, but which the frontend happens to display in visual forms.

(Code itself is also data, except that if a symbol in the data has an associated transformation rule, that transformation rule is applied. This mechanism is how you make data do 'variables' and 'functions'. These variables and functions work more or less the same as they would in a common programming language, but the underlying mechanism is nothing but data transformations.)

[1] http://reference.wolfram.com/mathematica/ref/EdgeDetect.html

Re: Programming paradigms that change how you think about coding

#102
post #83

Earlier quoted context omitted.

Yes int x (>= 0, Dependent Type systems don't have to know what is possible keyboard input. They have to know what type the getKeyboardType function returns (and the parseInt), and if it's possible for that type to be out of the range of your constraint. If that's so, it throws an error. At that point you simply have to compose your types and functions so they match the constraint, and you know you are safe.

> if it's possible for that type to be out of the range of your constraint....If that's so, it throws an error. If I'm understanding you correctly, that would seem to take a lot of the power out of dependent type systems. Almost all values will eventually depend on input from the outside world, so it seems you'd rarely have a chance to use your dependent type system. Haskell (which doesn't have dependent types baked…

I think a better example is the partial functions in Haskell like head or tail. They throw an exception on empty lists. You could make it return maybe [a] or a default value, but a common usage of head might be where you know yourself that the list is non-empty. Like:

head . sort . f

where f is a function that you know returns a non-empty list, perhaps because that's just the way it operates or because you checked it further up. You know that head won't crash since any reasonable implementation of sort returns a list which is just as long as the one that was given to it. Similarly:

tail . sort . f

won't crash. Moreover, the list will still be sorted, since you just removed the head of the list.

This is all known to you, but you can't necessarily easily show it to the compiler.

I don't know the motivation for wanting to know the input before it is given. Putting it in a Maybe is the only sensible option. Some things are just not known/unknowable before running a program. You wouldn't even need to run the program if you could prove absolutely anything about the properties of a program.

Will the user input bleed through to all corners of the program? Well, why would it? You could have a sanitizing loop that refuses to pass "wrong" data to the pure (and dependently typed!) parts of the program, just looping over and over until the correct input is given.

Re: Programming paradigms that change how you think about coding

#103
I'm a huge fan of the declarative programming paradigm, but outside of Regexp and SQL and a handful of other DSLs, it's dead. Its death should be a case study in Open Source strategy: It died because it became boring before it became useful. SQL and Regexp have stuck around because they did something useful immediately.

I think that any future that the Declarative paradigm has within general purpose languages is the kind applied by compilers. For example, x = (a + b) - a can be reduced to x = a or even eliminated altogether with subsequent in-scope references to x being replaced with a. Another example is dead code elimination. These forms of declarative let you use an imperative or functional language immediately but gently introduce you to declarative benefits without having to deal with all the mind bending that is necessary to optimize pure declarative code.

Re: Programming paradigms that change how you think about coding

#104
post #63

A thought on dependent types: Can a dependent type system catch all type errors at compile time? For example, suppose I write the following (in pseudo-code): // Variable x is an integer greater than or equal to 0 and less than 256. int x (>=0, I can imagine how a compiler could catch that kind of error. But that's trivial. What happens in programs like this: int x (>= 0, Now the compiler can't know for sure whether t…

As others have noted, it's less about the compiler running code and making determinations as it is using extremely robust, information-rich types. For instance, the Vector type in Idris has two type parameters (not sure that's the correct term to use in this case): the first is the type of the element contained by the vector, and the second is the length of the vector. Operations that increase the size of the vector are guaranteed to return a non-empty vector. They might look something like this:

    (::) : a -> Vector a n -> Vector a (S n)
This says that an element cons a vector of any size results in a vector that is guaranteed to be non-empty. This means that you could safely write a function to retrieve the head of a list by restricting the type to lists that are guaranteed to be non-empty.

    head : Vector a (S n) -> a
Something like the following pseudo-code in a REPL(I don't actually know a thing about Idris):

    let nums : Vector Int n = []
    head nums
...would fail to compile, since the second type param to nums is simply `n` rather than `(S n)`, as required by our `head` function. All of the things you would expect from Haskell like the power of ADTs and pattern-matching apply here, so you get even more compile time type-soundness.

Re: Programming paradigms that change how you think about coding

#105
post #76

Earlier quoted context omitted.

Yes, Depently Typed languages can. They are full blown theorem provers...

Gödel guarantees that full-blown theorem provers, even with human guidance, cannot prove all true statements about sufficiently rich systems. 'Sufficiently rich' here just means 'includes Peano arithmetic', and you can find tons of tutorials for doing just that (Church encoding) in even the simplest type systems. (This bit of pure-mathematics wonkery ignores the substance of jarrett's question, though; one doesn't ne…

I wonder; have mathematicians found something that they know with every fibre of their being is true, but that they can't prove? And do they then try to prove it in another formal system instead?

Or do these unprovable-but-true facts mostly elude them, since they are operating in a formal system that 'hides' them?

Re: Programming paradigms that change how you think about coding

#106
post #92

Earlier quoted context omitted.

> Can a dependent type system catch all type errors at compile time? I'm probably not clever enough, but someone could probably prove that'd be equivalent to solving the halting problem. It seems impossible. > because the value of getKeyboardInput could be anything That makes my brain hurt. I think implicitly narrowing the type is stylistically better. Maybe just adding a dependent type declaration: x = (parseInt(get…

> I'm probably not clever enough, but someone could probably prove that'd be equivalent to solving the halting problem. It seems impossible. Guessing you're plenty clever...just didn't think about it enough :) Simply transform a program into one which halts on a type error rather than whatever else it might be doing. Done.

Consider the case of a simple type system, where any statement in the language is of either type "true" or type "false"....

Re: Programming paradigms that change how you think about coding

#107
Dependent types are a wonderful idea so long as I can do a couple things with them: 1. Copy paste them without complications. i.e. "non-null" requires no code that relies on specific variables unless there's a logical conflict (which variable?) 2. If it's a known failure, give me a warning. If it's an unknown failure, let me choose how to deal with it, again in a neutral fashion that I could simply say @Notnull and it would just skip the code if the variable is null.

Re: Programming paradigms that change how you think about coding

#108
Declarative programming is a great one, almost a magical experience.

"It feels like I am sitting at the controls of a quantum computer. I've got all my qubits (terms) all wired together in some complicated expression and when power is applied every qubit will instantly collapse out of superposition and crystallize into a perfect answer."

(From something I've been working on, http://kmkeen.com/sat/ )

Re: Programming paradigms that change how you think about coding

#109
post #83

Earlier quoted context omitted.

Yes int x (>= 0, Dependent Type systems don't have to know what is possible keyboard input. They have to know what type the getKeyboardType function returns (and the parseInt), and if it's possible for that type to be out of the range of your constraint. If that's so, it throws an error. At that point you simply have to compose your types and functions so they match the constraint, and you know you are safe.

> if it's possible for that type to be out of the range of your constraint....If that's so, it throws an error. If I'm understanding you correctly, that would seem to take a lot of the power out of dependent type systems. Almost all values will eventually depend on input from the outside world, so it seems you'd rarely have a chance to use your dependent type system. Haskell (which doesn't have dependent types baked…

> But how do you get from untrusted, potentially invalid data into the world of trusted, guaranteed-valid data?

The main point is that you can write provably correct validation functions. Then your program can branch on the result of that function, and in the "success" branch you can process data further, as usual, assuming (justifiably) the validity of the data. It doesn't make any difference whether some particular piece of data was validated runtime or compile time, so far as the validation does what you want it to do and the types reflect the result in sufficient detail. Verified programming is mostly not about trying to check everything compile time, but rather checking that when our program eventually runs, it does the right thing. Take the following pseudo-Idris-Agda function:

    boundsCheck : Nat -> (bound : Nat) -> Maybe (n : Nat ** n 
This either returns the input natural number (if the input is within bounds) along with a proof of being in bound, or returns failure. The type of the function makes it impossible to write a function body such that the returned number is not within the supplied bounds. Any subsequent code in the program may work with the output of this function and be utterly sure that the "n" in "Just (n, p)" observes the bound, and the may also safely omit any further checks for the same bound or greater bounds. Or here's a standard Idris function:

    filter : (a -> Bool) -> Vect n a -> (n : Nat ** Vect n a)
    filter p [] = (_ ** [])
    filter p (x :: xs) with (filter p xs)
        | (_ ** xs') = if p x then (_ ** x :: xs') else (_ ** xs')
This is a plain old filter, except that it works on a length-indexed list type "Vect". We have no idea (compile time) how long the resulting filtered Vect will be. So we return a dependent pair containing some Nat and a Vect such that its length is equal to that Nat.

You might think that this is rather boring, since we could have used a list type not indexed by its length, and then just computed the length whenever we needed it runtime. Except there is a big difference. It is impossible to return a value of type

    (n : Nat ** Vect n a)
from any function, such that in the value the Nat index does not equal the length of the vector! You could slap together a plain list and a Nat in a tuple, and then say: "This a list coupled with its length. Let's try to preserve this invariant by writing code carefully". You could also trying hiding the possibly unsafe internals behind some OOP class firewall, but even that doesn't protect you from writing the internals incorrectly. Dependent types can protect you even then; they only fail to prevent you from writing the wrong types in the first place.
Post reply on HN