Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Colonel Taint
Mar 14, 2004


But... what's the different between 'extending the language' and building an API/library then? I've read McCarthy's paper and I realize the whole thing is built from a fairly basic foundation, which is cool in itself, but I keep getting hung up about that particular choice of words, especially when multiple authors use it to refer to defining functions. I can kind of seeing it make sense when talking about macros. Maybe I just haven't reached lisp nirvana yet.

Colonel Taint fucked around with this message at 23:01 on May 3, 2017

Adbot
ADBOT LOVES YOU

Arcsech
Aug 5, 2008

Colonel Taint posted:

But... what's the different between 'extending the language' and building an API/library then? I've read McCarthy's paper and I realize the whole thing is built from a fairly basic foundation, which is cool in itself, but I keep getting hung up about that particular choice of words, especially when multiple authors use it to refer to defining functions. I can kind of seeing it make sense when talking about macros. Maybe I just haven't reached lisp nirvana yet.

I mean, that's basically the point: Adding new syntax is as simple as adding a library. Consider Clojure's arrow macro:
code:
(-> "a b c d" 
           .toUpperCase 
           (.replace "A" "X") 
           (.split " ") 
           first)
which expands to:
code:
(first (.split (.replace (.toUpperCase "a b c d") "A" "X") " "))
In a language like Java, a pipeline operator like that would need to be part of the language. But in Lisp, it's just a macro, so if you need more complex behavior, that's just a library. (Yes, in ML-alikes you can do this with operators, but that's just because the syntax supports automatic currying. This is just one example.)

Another good example is Clojure's core.async, which implements a concurrency model similar to Go, but with no need for language support: It's just a library. And you can do this all pretty easily and safely, because the language is written in the same data structures you use for "normal" data.

So yes, it's "just" macros, but it's really, really drat good macros.

xtal
Jan 9, 2011

by Fluffdaddy
core.async and core.typed are my favourite examples here. Goroutines and the type system are libraries not language

crazypenguin
Mar 9, 2005
nothing witty here, move along

Colonel Taint posted:

what's the different between 'extending the language' and building an API/library then?

There's a lot of bullshit around the phrase. My phd is on language extension, and at one point I quote someone from the 70s talking about how the "class" keyword extends the language with new types in this newfangled Simula language!

Assume the phrase means nothing, along with other phrases like "strong/weak typing". Look at what they're actually doing instead. SICP was originally authored in 79, it could well be "marvel at functions, you GOSUB BASIC-plebs!"

crazypenguin fucked around with this message at 16:52 on May 6, 2017

Pollyanna
Mar 5, 2005

Milk's on them.


I just kinda assume that any of the magical stuff that Lisp does was subsumed into modern languages long ago, so none of it is impressive or out of the ordinary for those of us using anything newer than COBOL.

Kilson
Jan 16, 2003

I EAT LITTLE CHILDREN FOR BREAKFAST !!11!!1!!!!111!

Pollyanna posted:

I just kinda assume that any of the magical stuff that Lisp does was subsumed into modern languages long ago, so none of it is impressive or out of the ordinary for those of us using anything newer than COBOL.

I think a lot of that stuff has finally been making it into more 'modern' languages only very recently, and each language seems to take different parts. That's why things like Greenspun's 10th rule exist: a lot of complicated software (including other programming languages, IMO), poorly reimplement parts of Common Lisp.

Roadie
Jun 30, 2013
The key thing with Lisp is that the data structure for a function is also exactly the same data structure as anything else.

Imagine your language of choice, being able to treat a function as an object (if you're not already able to), but then also being able to map and index on it as if it was an array.

Doc Hawkins
Jun 15, 2010

Dashing? But I'm not even moving!


Roadie posted:

Imagine your language of choice, being able to treat a function as an object (if you're not already able to), but then also being able to map and index on it as if it was an array.

That's JavaScript.

Arcsech
Aug 5, 2008

Doc Hawkins posted:

That's JavaScript.

Interestingly, what became JavaScript was originally supposed to be Scheme, until Netscape decided that their scripting language had to look like Java.

Basically JavaScript is what you get if you take Scheme and gently caress it up horribly by trying to squeeze it into a Java suit over the course of 10 days

Arcsech fucked around with this message at 05:51 on May 7, 2017

Siguy
Sep 15, 2010

10.0 10.0 10.0 10.0 10.0
This may not be exactly what's being discussed, but lisp macros always seemed like some sort of arcane lost magic to me until I read this (lengthy) Common Lisp tutorial that walks through building a simple database with functions and then finally uses macros to make an SQL-ish language for writing queries to the database.

I found the article so interesting that I briefly tried learning Common Lisp before realizing it's kind of ancient. I ended up trying Racket instead, which I enjoyed but had no real use for.

Bongo Bill
Jan 17, 2012

Clojure is the most modern of the mature lisps, I think.

Roadie
Jun 30, 2013

Doc Hawkins posted:

That's JavaScript.

In JS, I can treat a function as an object, but I can't go in and muck around with the AST of that function as a normal part of the language.

Athas
Aug 6, 2007

fuck that joker

Roadie posted:

In JS, I can treat a function as an object, but I can't go in and muck around with the AST of that function as a normal part of the language.

You also cannot do that in any recent version of Lisp. You can get access to the syntax tree at compile-time via macros, but you can't inspect function objects at runtime (unless you made sure to squirrel away their definition somewhere yourself).

Pollyanna
Mar 5, 2005

Milk's on them.


I will admit that I have trouble understanding the advantage of having direct access to the AST of a function.

Bongo Bill
Jan 17, 2012

Pollyanna posted:

I will admit that I have trouble understanding the advantage of having direct access to the AST of a function.

At runtime, there isn't much.

At compile-time, being able to emit functions programmatically is a very powerful form of metaprogramming.

Destroyenator
Dec 27, 2004

Don't ask me lady, I live in beer
A lot of the appeal is that the core language is based around list manipulation and the core data structures are all list based. The entire AST is not only representable as lists but it's most obvious form is those same list structures, so it makes manipulating it straightforward and more "natural" than in any other language.

Colonel Taint
Mar 14, 2004


Hm, I think the idea is starting to set in. It's pretty cool to think of writing programs in that way. Like pure lisp basically - to use a loaded word - transcends other languages in its directness of interpretation. Like it's something of a polar opposite of - but at the same time analogous to - assembly language.

On a side note, going through SICP and doing all the little number theory exercises has been good fun. It's been pretty gentle so far so I'm hoping I can keep up with the rest as I go.

KernelSlanders
May 27, 2013

Rogue operating systems on occasion spread lies and rumors about me.
Has anyone used Finch? How was it? How does it compare to AkkaHttp or http4s?

loose-fish
Apr 1, 2005
While playing around with generic execution models I ran into the following, a typeclass with a function that returns another instance of that typeclass:
code:
class Processor a where
    process :: Processor b => a -> Int -> b

data P = P Int

instance Processor P where
    process (P x) y = P y
-- can't return P because it's no Processor

-- more precise, but pointless
instance (Processor P) => Processor P where
    process (P x) y = P y
Is there a way to achieve something like this in Haskell? Type families maybe? I'm not up to speed on all extensions...

fomo sacer
Feb 14, 2007

loose-fish posted:

While playing around with generic execution models I ran into the following, a typeclass with a function that returns another instance of that typeclass:
code:
class Processor a where
    process :: Processor b => a -> Int -> b

data P = P Int

instance Processor P where
    process (P x) y = P y
-- can't return P because it's no Processor

-- more precise, but pointless
instance (Processor P) => Processor P where
    process (P x) y = P y
Is there a way to achieve something like this in Haskell? Type families maybe? I'm not up to speed on all extensions...

The way you've written Processor is requiring that a Processor can be turned into any other instance of Processor by calling process on it, i.e. the return type of process needs to be polymorphic, but when you wrote the instance for P, you fixed the return type as P.

You can fix this by requiring Processors to provide you with a generic constructor function:
code:
class Processor a where
  fromInt :: Int -> a
  process :: Processor b => a -> Int -> b
  
data P = P Int

instance Processor P where
  fromInt = P
  process (P a) b = fromInt b
Alternatively, you can use multi-parameter type classes to represent the idea that one type can be processed into another type:
code:
{-# Language MultiParamTypeClasses #-} 
class Processor a b where
  process :: a -> Int -> b
  
data P = P Int
data Q = Q Int

instance Processor P Q where
  process (P a) b = Q (a*b)

instance Processor Q P where
  process (Q a) b = P (a+b)
In this case, GHC may have trouble with type inference unless you sprinkle type annotations all over your code. You can avoid this with functional dependencies, at the cost of some flexibility:
code:
{-# Language MultiParamTypeClasses #-}
{-# Language FunctionalDependencies #-} 
class Processor a b | a -> b where -- a determines b. b -> a is also possible
  process :: a -> Int -> b
  
data P = P Int
data Q = Q Int

instance Processor P Q where
  process (P a) b = Q (a*b)

instance Processor Q P where
  process (Q a) b = P (a+b)

instance Processor P P where -- not allowed, we already have an instance of Processor P _
  process = undefined

fomo sacer fucked around with this message at 00:57 on May 21, 2017

loose-fish
Apr 1, 2005
Thanks for clearing that up! I had a fundamental misconception of type classes, guess the 'forall' should've tipped me off...

I'm trying to find a solution for processors with arbitrary local state, and a function to apply an event to the global state according to the current processor. (I say 'State' but I don't care if it's in the State Monad, it's just what I'm using here).
This is the best I've come up with so far:
code:
import Control.Monad.State (State, put)

type Model = String
type Event = Int

data AppState = AppState { _model :: Model
                         , _processor :: Event -> State AppState ()
                         }

class Processor a where
    process :: a -> Event -> State AppState ()


data P = P Int

instance Processor P where
    process (P 0) x = put $ AppState "foo" (process $ Q x (x * 2))  -- change processor to Q
    process (P _) x = put $ AppState (show x) (process $ P x)


data Q = Q Int Int

instance Processor Q where
    process (Q 0 0) x = put $ AppState "bar" (process $ P x)  -- change processor to P
    process (Q _ _) x = put $ AppState (show x) (process $ Q x (x * 2))
I know the simple way to do it would be something like this:
code:
data Processor = P Int | Q Int Int

process :: Processor  Model -> Event -> (Processor, Model)
process (P p) m = processP p m
process (Q q) m = processQ q m
But this would make it impossible to add new Processors in separate modules...

Guacamayo
Feb 2, 2012
How do I install Scheme on Windows? I trying to go through SICP.

Guacamayo fucked around with this message at 05:34 on Jun 2, 2017

Bongo Bill
Jan 17, 2012

Guacamayo posted:

How do I install Schemeon Windows? I trying to go through SICP.

You might try Racket, which can be configured to use R6RS or other Scheme dialects rather than its own custom dialect.

Guacamayo
Feb 2, 2012

Bongo Bill posted:

You might try Racket, which can be configured to use R6RS or other Scheme dialects rather than its own custom dialect.

Cool Im gonna try that out

meatbag
Apr 2, 2007
Clapping Larry
FWIW, I found SICP to be wildly overrated, but I'm not good with math, and most of the early examples at least involve math stuff.

You might want to look at How to Design Programs for a more updated textbook, if you are trying to learn functional programming in general and not Scheme in particular.

Colonel Taint
Mar 14, 2004


I've been going through SICP over the past month or two using DrRacket and it's pretty good. I'm currently up to section 2.2. Along with scheme R6RS, you can grab the sicp language pack - using this became especially useful when list structures were introduced.

Also I don't know what version of the text you're reading, but there's a pretty good HTML version available on github: https://sarabander.github.io/sicp/html/

e: Trees constructed from lists are the devil

Colonel Taint fucked around with this message at 19:50 on Jun 2, 2017

205b
Mar 25, 2007

Haskell question for you guys - I'm trying to build a cyclic data structure by "tying the knot", and my code compiles but then just hangs when I try to run it.

First, some context. I'm trying to build a doubly-linked binary tree. Each node in the tree references a state of a hash function, and non-root nodes include a block of input bytes such that hashing the input (starting from the node's state) yields the parent node's state.

It doesn't matter what the actual hash states are, only that this relationship between states holds. As a result, it's much easier to build the tree from the leaves up than from the root down. (Given two states, we can search for inputs that will produce a collision on any parent state, but if we start from the root, we have to search for states and inputs that yield the parent state.)

Here's the code:

(edit: more concise example below)

Sorry for the messiness! I'm still picking up the language and I think I'm probably overdoing it on pattern matching.

205b fucked around with this message at 23:03 on Jun 5, 2017

gonadic io
Feb 16, 2011

>>=
Your pattern matching is mostly fine, although honestly given that the chunksOf 2 is hard-coded to be 2 I recommend getting it to just return a pair. Lists with statically-known lengths are a code smell.

In terms of your problem it's pretty difficult to know exactly where the problem is, could you post a smaller snippet that still demonstrates the bug please?

Also the Debug.Trace module is handy for this kind of thing, although you might have to be careful that you don't fully evaluate the entire tree when printing at each step, or you'll just move your problem to there.

dirby
Sep 21, 2004


Helping goons with math

gonadic io posted:

although honestly given that the chunksOf 2 is hard-coded to be 2 I recommend getting it to just return a pair. Lists with statically-known lengths are a code smell.

chunksOf 2 returns a list of lists of length 2 except maybe there could be a list of length 1 at the end if you didn't statically know that the argument had even length. Therefore, the best you could do is make it into a function that returns a list of pairs and does something counterintuitive at the end for odd-length inputs.

205b
Mar 25, 2007

dirby posted:

chunksOf 2 returns a list of lists of length 2 except maybe there could be a list of length 1 at the end if you didn't statically know that the argument had even length. Therefore, the best you could do is make it into a function that returns a list of pairs and does something counterintuitive at the end for odd-length inputs.

Right, if I run out of elements I get a final list of length 1 and then pattern matching fails at runtime. Even if chunksOf gave me tuples, the compiler wouldn't be able to guarantee me an input list of even length. I couldn't think of an unobtrusive way around that.

gonadic io posted:

Your pattern matching is mostly fine, although honestly given that the chunksOf 2 is hard-coded to be 2 I recommend getting it to just return a pair. Lists with statically-known lengths are a code smell.

In terms of your problem it's pretty difficult to know exactly where the problem is, could you post a smaller snippet that still demonstrates the bug please?

Also the Debug.Trace module is handy for this kind of thing, although you might have to be careful that you don't fully evaluate the entire tree when printing at each step, or you'll just move your problem to there.

That's already a huge help because I didn't know about Debug.Trace - I'd been putting conditional error calls in for debugging :haw:

I cut out all the hash stuff, so now nodes just have an integer value, and parent values are the sum of child values. I also cut the number of leaves to two, so it should be a three-node tree.

code:
data Node = Node Integer Relationships
data Relationships = Root Children | Branch Children Parent | Leaf Parent
data Children = Children Node Node
data Parent = Parent Node


k :: Integer
k = 1


main :: IO ()
main = putStrLn $ show $ length leaves  -- hangs


leaves :: [Node]
leaves = concat leafPairs
    where
        valuePairs = chunksOf 2 [1..2^k]                   -- I can trace this
        parentValues = map sum valuePairs                  -- and this
        parents = getParents $ zip parentValues leafPairs  -- but not this
        leafPairs = zipWith build parents valuePairs       -- or this
        build parent [valueA, valueB] = [Node valueA r, Node valueB r]
            where r = Leaf $ Parent parent


-- We're down to one parent, so make it a root
getParents :: [(Integer, [Node])] -> [Node]
getParents [(value, [childA, childB])] = [Node value $ Root children]
    where children = Children childA childB

sink
Sep 10, 2005

gerby gerb gerb in my mouf
I'm starting to write some small DSLs for work (and well, for fun. It's really fun). I'm building interpreters with Scala and the atto parser combinator library (https://github.com/tpolecat/atto), which is amazing. The DSLs aren't meant to be used from within Scala, it's just that the interpreter / compiler is implemented in that language. My DSLs are declarative but have features like variable dereferencing, function application, native lists, some basic typechecking, etc.

I think I did an okay job implementing them, but I was wondering if anyone can point me to some modern literature on compiler and interpreter implementation. I'm aware of some of the papers on Free and extensible effects, but I am not using those techniques here.

Bongo Bill
Jan 17, 2012

Typing the Technical Interview

quote:

Haskell is a dynamically-typed, interpreted language.

Tanners
Dec 13, 2011

woof

That entire series of posts is gold

Good Will Hrunting
Oct 8, 2012

I changed my mind.
I'm not sorry.
What's the most efficient way in Scala to do pass a String through a number of possible regex pattern and match based on which pattern is matched?

code:
  val A: Regex = "([^/]+)/([.0-9]+) \\((iPhone|iPad); iOS ([.0-9]+); Scale/[.0-9]+\\)".r
  val B: Regex = "(69 420 gains)".r

  def resolveCustomRegex(ua: String): Data = {
    ua match {
      case A => //need the captured groups here
      case B => //need the captured groups here
    }
  }
This seems like it would be less efficient than piping the two together like ([^/]+)/([.0-9]+) \\((iPhone|iPad); iOS ([.0-9]+); Scale/[.0-9]+\\) | (69 420 gains) but in the second case capturing match groups doesn't seem feasible. Also I can't figure out how to get the groups in each case's corresponding function

emoji
Jun 4, 2004
I'm working through a compiler book with Racket and I'm liking it a lot.

Kilson
Jan 16, 2003

I EAT LITTLE CHILDREN FOR BREAKFAST !!11!!1!!!!111!

emoji posted:

I'm working through a compiler book with Racket and I'm liking it a lot.

What book?

emoji
Jun 4, 2004
The Elements of Computing Systems and the companion site http://www.nand2tetris.org which works through building a general purpose computer. The hardware is built with an HDL from nand gates and simulated which interprets a machine code, on top of which you write an assembler, vm, compiler, os, etc. Afterwards I'll probably try a more focused compiler text.

Carbon dioxide
Oct 9, 2012

Hey folks, I'm currently familiar with Java 8's support for functional programming with some basic knowledge of rx Java.

I'm moving to a job where I will spend a lot of time in Scala, with Akka. Can anyone suggest any good tutorials to get started with that? Thanks.

Hughmoris
Apr 21, 2007
Let's go to the abyss!
Any recommended F# blogs and tutorial videos? I tried to wrap my head around FP using Haskell and it just wasn't taking, so I figure I'll give F# a whirl.

Adbot
ADBOT LOVES YOU

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

F# for fun and profit is always the go-to - it has a basic tutorial path but there's a ton of content in there

  • Locked thread